Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 61 additions & 6 deletions integrations/bricks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ A WordPress plugin that integrates the [SLASHED](https://github.com/codeslash-de
## Features

- **CSS Loading** - Automatically enqueues the SLASHED CSS bundle on the frontend and within the Bricks editor iframe
- **Variable Pickers** - Registers all 587+ SLASHED CSS custom properties for use in Bricks variable pickers and the code editor autocomplete
- **Class Autocomplete** - All `.sf-*` layout classes and `.is-*` state classes appear in the Bricks class input with organized categories
- **Color Palette** - Synchronizes SLASHED color tokens (brand scales, status, and semantic colors) with the Bricks global color palette
- **Variable Pickers** - Registers every `--sf-*` CSS custom property declared in the active bundle (~600 in `optimal`, ~700 in `full`) with the Bricks variable pickers and code editor autocomplete, organized into category groups
- **Class Autocomplete** - Registers every `.sf-*` layout/utility class and `.is-*` state class declared in the active bundle with the Bricks class input, organized into "SLASHED Layout" and "SLASHED State" categories
- **Color Palette** - Synchronizes every `--sf-color-*` token (brand scales including alpha steps, status, and semantic colors) with the Bricks global color palette
- **Dynamic Detection** - The integration parses the loaded CSS bundle at runtime, so registrations stay in sync with whichever bundle (`essential` / `optimal` / `full`) and SLASHED release is active. There is no hand-curated list to drift out of date.

## Requirements

Expand Down Expand Up @@ -96,6 +97,32 @@ add_filter( 'slashed_bricks/registered_variables', function( $variables ) {
} );
```

#### `slashed_bricks/inventory`

Replace the resolved inventory wholesale. Useful for tests, custom forks of the framework, or sites that want to ship their own token list. Expects an array shaped `['variables' => string[], 'sf_classes' => string[], 'is_classes' => string[]]`.

```php
add_filter( 'slashed_bricks/inventory', function( $inventory ) {
$inventory['variables'][] = '--sf-color-my-custom';
return $inventory;
} );
```

#### `slashed_bricks/inventory_local_path`

Override the local CSS path the inventory parses. The filter is authoritative:

- Return a string to use a specific path (skipping the default candidates).
- Return `false` to skip local resolution entirely (forcing a CDN fetch).
- Return `null` (the default) to use the bundled candidate paths.

```php
// Use a child-theme copy of the bundle.
add_filter( 'slashed_bricks/inventory_local_path', function() {
return get_stylesheet_directory() . '/assets/slashed.optimal.css';
} );
```

#### `slashed_bricks/color_categories`

Filter which color categories to include in the palette.
Expand All @@ -115,6 +142,13 @@ add_filter( 'slashed_bricks/color_categories', function( $categories ) {
```
integrations/bricks/
slashed-bricks.php Main plugin bootstrap (guards, constants, loader)
data/inventory.json Built-in fallback inventory (used when no
local CSS or CDN is reachable)
includes/class-css-parser.php Pure parser: declared --sf-* properties +
.sf-/.is- class selectors from a CSS string
includes/class-inventory.php Resolves the active CSS bundle (local file,
then CDN with transient cache, then fallback)
and categorizes variables by prefix family
includes/class-enqueue.php CSS enqueue for frontend + editor iframe
includes/class-variables.php Variable registration for builder pickers
includes/class-classes.php Class registration for autocomplete
Expand All @@ -129,11 +163,32 @@ integrations/bricks/

2. **Enqueue** (`class-enqueue.php`) - Hooks into `wp_enqueue_scripts` to load the SLASHED CSS bundle. Since Bricks fires `wp_enqueue_scripts` in its editor iframe context, this single hook covers both frontend and builder preview.

3. **Variables** (`class-variables.php`) - Registers organized variable groups via `bricks/builder/i18n` and provides code editor autocomplete data via `bricks/code/get_code_signatures`.
3. **Inventory** (`class-inventory.php`) - The single source of truth for "what does the framework actually ship?" It locates the active CSS bundle (preferring a local file, falling back to the CDN, then to a built-in `data/inventory.json`), parses it once via `class-css-parser.php`, and caches the result via WordPress transients (keyed by file mtime or URL). All three registration classes share one process-local cache so a single page load incurs at most one parse.

4. **Variables** (`class-variables.php`) - Pulls the categorized variable list from the inventory and registers organized variable groups via `bricks/builder/i18n` and code editor autocomplete data via `bricks/code/get_code_signatures`.

5. **Classes** (`class-classes.php`) - Pulls `.sf-*` and `.is-*` class lists from the inventory and registers them as locked global classes via `bricks/setup/control_options`, tagged "SLASHED Layout" and "SLASHED State" respectively.

6. **Colors** (`class-colors.php`) - Pulls every `--sf-color-*` from the inventory, splits them into brand-family categories (one per brand: Primary, Secondary, Tertiary, Action, Neutral, Base), Status, and Semantic groups, and injects them via `bricks/setup/control_options`. Swatches reference CSS variables (`var(--sf-color-*)`) rather than hardcoded values, so they adapt to theme customization and dark mode.

### Inventory Resolution Order

The inventory class tries these sources in order, stopping at the first one that succeeds:

4. **Classes** (`class-classes.php`) - Registers all layout and state classes as locked global classes via `bricks/setup/control_options`, making them available in the class picker with categorized organization.
1. **Local file** at `dist/slashed.optimal.css` (relative to the plugin) - covers symlink/in-repo development and copy-installs that include the `dist/` folder. Cached as a transient keyed by file mtime, so edits invalidate automatically.
2. **CDN URL** - whatever `slashed_bricks_get_css_url()` returns, fetched via `wp_remote_get` and cached as a transient for one day.
3. **Built-in fallback** - `data/inventory.json`, generated at release time from `dist/slashed.optimal.css` by `scripts/gen-bricks-inventory.js`. This keeps the plugin functional on hosts that block outbound HTTP.

5. **Colors** (`class-colors.php`) - Injects color entries into the Bricks global color palette via `bricks/setup/control_options`. Colors reference CSS variables (`var(--sf-color-*)`) rather than hardcoded values, so they adapt to theme customization and dark mode.
You can short-circuit step 1 with the `slashed_bricks/inventory_local_path` filter, or replace the resolved inventory entirely with the `slashed_bricks/inventory` filter.

### Regenerating the Fallback Inventory

The fallback JSON ships with the plugin and must be regenerated whenever the framework adds or removes tokens or classes. The build script does this automatically:

```bash
npm run build # rebuilds dist/ AND regenerates inventory.json
npm run bricks:inventory # only regenerate inventory.json
```

## CSS Bundle

Expand Down
67 changes: 66 additions & 1 deletion integrations/bricks/assets/admin-page.css
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,62 @@
color: #646970;
}

/* Color Fields */
/* Color Fields - paired HEX picker + Advanced raw input */
.slashed-color-row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 12px;
}

.slashed-color-row .slashed-color-input {
display: flex;
align-items: center;
}

.slashed-color-row .slashed-color-input[hidden] {
display: none;
}

.slashed-color-row .slashed-color-raw {
min-width: 280px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 12px;
}

.slashed-color-row .slashed-color-toggle {
font-size: 12px;
color: #2271b1;
cursor: pointer;
background: none;
border: 0;
padding: 4px 0;
text-decoration: underline;
}

.slashed-color-row .slashed-color-toggle:hover,
.slashed-color-row .slashed-color-toggle:focus {
color: #135e96;
}

/* Toggle button label visibility - driven by data-mode on the row */
.slashed-color-row .slashed-color-toggle__label-hex,
.slashed-color-row .slashed-color-toggle__label-raw {
display: none;
}
.slashed-color-row[data-mode="hex"] .slashed-color-toggle__label-hex {
display: inline;
}
.slashed-color-row[data-mode="raw"] .slashed-color-toggle__label-raw {
display: inline;
}

.slashed-color-row .description {
flex-basis: 100%;
margin: 0;
}

/* Legacy single-input colors (kept for any custom integrations) */
.slashed-color-field-wrap {
display: flex;
align-items: center;
Expand Down Expand Up @@ -187,6 +242,16 @@
color: #646970;
}

.slashed-range-group .slashed-range-unit {
min-width: 24px;
font-size: 12px;
color: #646970;
}

.wrap .slashed-tab-content select {
min-width: 180px;
}

/* Live Preview Panel */
.slashed-preview-panel {
border: 2px solid #1d2327;
Expand Down
125 changes: 97 additions & 28 deletions integrations/bricks/assets/admin-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,50 +43,85 @@
}

/**
* Initialize WordPress color pickers on .slashed-color-field inputs.
* Initialize the color picker on every paired HEX/Advanced color row.
*
* Note: Since SLASHED uses oklch() color values, the WordPress color picker
* serves as a visual reference and inspiration palette. The actual saved value
* is the oklch() string entered in the text input.
* Each row contains:
* - .slashed-color-hex - the HEX input wired to wpColorPicker
* - .slashed-color-raw - the Advanced (oklch / any CSS color) input
* - .slashed-color-toggle - swaps which one is active/visible
*
* The active mode is recorded on the row's data-mode attribute, set
* server-side based on the saved value's shape (HEX vs anything else).
*/
function initColorPickers() {
if ( ! $.fn.wpColorPicker ) {
return;
}

// Curated palette of the rough HEX equivalents of SLASHED's
// default oklch tokens - shown as quick-pick chips in the picker
// dropdown. Real chosen values are still written to the input.
var palette = [
'#4338ca', // primary-ish
'#1e293b', // secondary-ish
'#7c3aed', // tertiary-ish
'#0891b2', // action-ish
'#64748b', // neutral-ish
'#16a34a', // success-ish
'#eab308', // warning-ish
'#dc2626' // error-ish
'#4338ca', '#1e293b', '#7c3aed', '#0891b2',
'#64748b', '#fafafa', '#16a34a', '#ca8a04',
'#dc2626', '#2563eb'
];

$( '.slashed-color-field' ).each( function() {
$( '.slashed-color-hex' ).each( function() {
var $input = $( this );

// Create a visual color reference picker alongside the text input.
var $pickerEl = $( '<input type="text" class="slashed-color-picker-visual">' );
$pickerEl.insertAfter( $input );

$pickerEl.wpColorPicker( {
$input.wpColorPicker( {
palettes: palette,
change: function() {
// The color picker is for visual reference only.
// Users should enter oklch() values in the text input.
// Defer one tick so wpColorPicker has finished writing
// the new value into the underlying input, then mirror
// the change into our dirty/preview pipeline.
window.setTimeout( function() {
markDirty();
updateLivePreview();
}, 0 );
},
clear: function() {}
clear: function() {
markDirty();
updateLivePreview();
}
} );
} );

// Add a helper note.
if ( ! $input.siblings( '.color-note' ).length ) {
$input.closest( 'td' ).find( '.wp-picker-container' ).after(
'<span class="color-note">Use oklch() values in the text field. The color picker is for visual reference only.</span>'
);
// Toggle button: swap which input is active for a given row.
$( document ).on( 'click', '.slashed-color-toggle', function( e ) {
e.preventDefault();
var $btn = $( this );
var $row = $( '#' + $btn.data( 'row' ) );
if ( ! $row.length ) {
return;
}
var current = $row.attr( 'data-mode' ) || 'hex';
var next = current === 'hex' ? 'raw' : 'hex';

$row.attr( 'data-mode', next );
$row.find( '.slashed-color-input--hex' ).attr( 'hidden', next !== 'hex' ? true : null );
$row.find( '.slashed-color-input--raw' ).attr( 'hidden', next !== 'raw' ? true : null );

// Clear the inactive input so the merged save logic resolves
// unambiguously to the user's chosen value.
if ( next === 'hex' ) {
var $raw = $row.find( '.slashed-color-raw' );
if ( $raw.val() !== '' ) {
$raw.val( '' );
}
} else {
// Switching to raw: clear the picker via its API so the
// saved hex doesn't compete with what the user types.
var $hex = $row.find( '.slashed-color-hex' );
if ( $hex.val() !== '' && $hex.wpColorPicker ) {
$hex.wpColorPicker( 'color', '' );
}
$row.find( '.slashed-color-raw' ).trigger( 'focus' );
}

markDirty();
updateLivePreview();
} );
}

Expand Down Expand Up @@ -125,19 +160,31 @@

var declarations = [];

// Resolve active color value per row: prefer the raw input when
// it has content, otherwise use the HEX input. Mirrors what the
// PHP sanitiser does on save, so the preview always matches.
function resolveColor( baseKey ) {
var raw = $( '#' + baseKey + '_raw' ).val();
if ( raw && raw.length ) {
return raw;
}
var hex = $( '#' + baseKey + '_hex' ).val();
return hex && hex.length ? hex : '';
}

// Collect color values.
var brandColors = [ 'primary', 'secondary', 'tertiary', 'action', 'neutral', 'base' ];
var statusColors = [ 'success', 'warning', 'error', 'info', 'danger' ];

brandColors.forEach( function( color ) {
var val = $( '#brand_' + color ).val();
var val = resolveColor( 'brand_' + color );
if ( val ) {
declarations.push( '--sf-color-' + color + '-light: ' + val );
}
} );

statusColors.forEach( function( color ) {
var val = $( '#status_' + color ).val();
var val = resolveColor( 'status_' + color );
if ( val ) {
declarations.push( '--sf-color-' + color + '-light: ' + val );
}
Expand Down Expand Up @@ -205,6 +252,28 @@
}
} );

// Contrast tab knobs (numeric, no unit).
[ 'contrast_bias', 'contrast_threshold', 'opacity_disabled' ].forEach( function( key ) {
var val = $( '#' + key ).val();
if ( val !== undefined && val !== '' ) {
declarations.push( '--sf-' + key.replace( /_/g, '-' ) + ': ' + val );
}
} );

// Focus ring metrics (px-suffixed).
[ 'focus_ring_width', 'focus_ring_offset' ].forEach( function( key ) {
var val = $( '#' + key ).val();
if ( val !== undefined && val !== '' ) {
declarations.push( '--sf-' + key.replace( /_/g, '-' ) + ': ' + val + 'px' );
}
} );

// Focus ring style (enum).
var ringStyle = $( '#focus_ring_style' ).val();
if ( ringStyle ) {
declarations.push( '--sf-focus-ring-style: ' + ringStyle );
}

// Build CSS string.
var css = '';
if ( declarations.length ) {
Expand Down
Loading
Loading