Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
baa6185
Add design tokens
amjadr360 Jul 21, 2026
0031ad1
Design tokens: semantic palette aliases, dieted picker, block-style p…
amjadr360 Jul 25, 2026
1a2e676
Design tokens: palette back to the six stock colours; referential col…
amjadr360 Jul 25, 2026
c0e02fe
Design tokens: three custom spacing vars (snug/cozy/block)
amjadr360 Jul 25, 2026
382bf6a
Design tokens: remove the font-size preset layer (stock scale only)
amjadr360 Jul 25, 2026
a5d100a
Add vibes and color palettes
amjadr360 Jul 29, 2026
3367461
Task 170: linen becomes Heath — warm-cream recruiting palette
amjadr360 Jul 31, 2026
b20553a
Design tokens: reduce the ported style set to natural-1, retro-1 and …
amjadr360 Jul 31, 2026
699439b
Vibe globals: data file plus its read endpoint under inc/
amjadr360 Jul 31, 2026
e50f202
Vibe globals: drop the provenance note from the payload
amjadr360 Jul 31, 2026
375fc31
Vibe globals: ship the manifest the endpoint reads, and drop two dead…
amjadr360 Aug 1, 2026
0f46ed3
Vibe globals: retro publishes a font-size scale instead of two clamps
amjadr360 Aug 1, 2026
6fdefe7
Vibe globals: retro pins 56rem/120rem
amjadr360 Aug 1, 2026
e1c3f6a
Heath and Butterscotch declare a slug
amjadr360 Aug 1, 2026
8647924
Playdate: a third vibe — cream, navy, orange, with background objects
amjadr360 Aug 3, 2026
5f43f3a
Fix the band-edge divider selector: background, not background-base
amjadr360 Aug 3, 2026
47fc37d
Playdate: background objects become masks, inked from the palette
amjadr360 Aug 3, 2026
133a1bd
Playdate: the scalloped edge decorates a tinted band, and now has one
amjadr360 Aug 3, 2026
e1b0433
Background objects: rotate per band, sit behind the copy, drift on sc…
amjadr360 Aug 3, 2026
3ecfe81
Restore theme.json formatting — the keyframes edit had reflowed the file
amjadr360 Aug 3, 2026
305e389
Playdate: the tinted band keeps both scalloped edges
amjadr360 Aug 3, 2026
da804fd
Playdate: the first band carries no background objects
amjadr360 Aug 3, 2026
6adb7f3
Playdate: the scalloped edge covers both tint carriers
amjadr360 Aug 3, 2026
0a445ad
Playdate: only a tinted band reserves room for its scalloped strips
amjadr360 Aug 3, 2026
4d3b69d
Playdate: distinct card tints — the first was identical to the wash
amjadr360 Aug 3, 2026
53fccb6
Playdate: navy accent and a lighter first card tint
amjadr360 Aug 3, 2026
c4184d2
Playdate's card tints become one family
amjadr360 Aug 3, 2026
3ba114e
Card alignment: a centering vibe centers the mark, not just the copy
amjadr360 Aug 3, 2026
074305c
Card centering applies to stacked cards only
amjadr360 Aug 3, 2026
bf8f3ea
Card alignment moves off the leaves onto the card preset's own css
amjadr360 Aug 3, 2026
f6587a3
playdate-1 keeps a full radius on every image position
amjadr360 Aug 3, 2026
a11fc0c
playdate-1 becomes marzipan-1
amjadr360 Aug 3, 2026
0d568d3
The vibe-globals manifest takes the new slug too
amjadr360 Aug 3, 2026
9942335
stipple-1: a plotted-panel vibe with staggered cards
amjadr360 Aug 4, 2026
5f1ff94
stipple-1 drops to standard density
amjadr360 Aug 4, 2026
aef46c9
stipple-1 takes the dissolving mark field
amjadr360 Aug 4, 2026
26a6b11
Remove stipple-1
amjadr360 Aug 4, 2026
75fd152
Add foundry-1, and a primary-and-background duotone per palette
amjadr360 Aug 4, 2026
8599f6e
Add a primary-and-background duotone to root theme.json
amjadr360 Aug 4, 2026
59c93f7
Every colorway gets its own primary-and-background duotone
amjadr360 Aug 4, 2026
dc2cf7c
foundry-1 image/gallery/media-text/cover presets carry a colour.duotone
amjadr360 Aug 4, 2026
ae2fcc5
Preset duotone moves to filter.duotone; $schema catches up to trunk
amjadr360 Aug 4, 2026
046a723
Pin the generated presets' $schema to wp/7.0
amjadr360 Aug 4, 2026
2a63735
foundry-1: block-node duotone, mono voice, 56/120rem measure
amjadr360 Aug 4, 2026
084271f
foundry-1 takes the staggered grid
amjadr360 Aug 4, 2026
731064a
Uppercase headings reach h1-h6
amjadr360 Aug 4, 2026
0bea74a
Restore retro-1's heading case; Foundry's wash steps away from its paper
amjadr360 Aug 4, 2026
482d975
Uppercase reaches h1-h6 on foundry-1; presets drop the inert duotone
amjadr360 Aug 4, 2026
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
7 changes: 7 additions & 0 deletions functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,13 @@ function extendable_register_pattern_categories() {
*/
require_once get_template_directory() . '/inc/animations.php';

/**
* Vibe globals: the read endpoint for this theme's per-vibe global theme.json settings
* (inc/ext-vibe-globals.json). Deliberately not under styles/, which WordPress auto-reads
* as selectable style variations.
*/
require_once get_template_directory() . '/inc/ext-vibe-globals.php';

/**
* Add primary-foreground duotone to extendify demo Site Logo block.
*
Expand Down
224 changes: 224 additions & 0 deletions inc/ext-vibe-globals.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
{
"version": 1,
"vibes": {
"foundry-1": {
"settings": {
"layout": {
"contentSize": "56rem",
"wideSize": "120rem"
}
},
"styles": {
"elements": {
"h1": {
"typography": {
"fontSize": "clamp(2.313rem, 2.313rem + ((1vw - 0.2rem) * 3.959), 4.688rem)"
}
},
"h2": {
"typography": {
"fontSize": "clamp(1.875rem, 1.875rem + ((1vw - 0.2rem) * 3.125), 3.75rem)"
}
}
},
"blocks": {
"core/image": {
"filter": {
"duotone": "var:preset|duotone|primary-and-background"
}
},
"core/cover": {
"filter": {
"duotone": "var:preset|duotone|primary-and-background"
}
}
}
}
},
"linen-1": {
"settings": {
"layout": {
"contentSize": "56rem",
"wideSize": "120rem"
},
"typography": {
"fontSizes": [
{
"slug": "small",
"size": "1.125rem",
"fluid": false
},
{
"slug": "medium",
"size": "1.25rem",
"fluid": false
},
{
"slug": "large",
"size": "2.25rem",
"fluid": {
"min": "1.5rem",
"max": "2.25rem"
}
},
{
"slug": "x-large",
"size": "2.8125rem",
"fluid": {
"min": "1.8125rem",
"max": "2.8125rem"
}
},
{
"slug": "xx-large",
"size": "3.5rem",
"fluid": {
"min": "2.1875rem",
"max": "3.5rem"
}
}
],
"fluid": {
"maxViewportWidth": "80rem"
}
}
},
"styles": {
"elements": {
"h1": {
"typography": {
"fontSize": "var(--wp--preset--font-size--xx-large)"
}
},
"h2": {
"typography": {
"fontSize": "var(--wp--preset--font-size--x-large)"
}
}
}
}
},
"marzipan-1": {
"settings": {
"typography": {
"fontSizes": [
{
"slug": "small",
"size": "1.125rem",
"fluid": false
},
{
"slug": "medium",
"size": "1.375rem",
"fluid": false
},
{
"slug": "large",
"size": "3rem",
"fluid": {
"min": "1.75rem",
"max": "3rem"
}
},
{
"slug": "x-large",
"size": "4rem",
"fluid": {
"min": "2.125rem",
"max": "4rem"
}
},
{
"slug": "xx-large",
"size": "5.25rem",
"fluid": {
"min": "2.625rem",
"max": "5.25rem"
}
}
],
"fluid": {
"maxViewportWidth": "80rem"
}
}
},
"styles": {
"elements": {
"h1": {
"typography": {
"fontSize": "var(--wp--preset--font-size--xx-large)"
}
},
"h2": {
"typography": {
"fontSize": "var(--wp--preset--font-size--x-large)"
}
}
}
}
},
"retro-1": {
"settings": {
"layout": {
"contentSize": "56rem",
"wideSize": "120rem"
},
"typography": {
"fontSizes": [
{
"slug": "small",
"size": "1.125rem",
"fluid": false
},
{
"slug": "medium",
"size": "1.375rem",
"fluid": false
},
{
"slug": "large",
"size": "3rem",
"fluid": {
"min": "1.75rem",
"max": "3rem"
}
},
{
"slug": "x-large",
"size": "4rem",
"fluid": {
"min": "2.125rem",
"max": "4rem"
}
},
{
"slug": "xx-large",
"size": "5.25rem",
"fluid": {
"min": "2.625rem",
"max": "5.25rem"
}
}
],
"fluid": {
"maxViewportWidth": "80rem"
}
}
},
"styles": {
"elements": {
"h1": {
"typography": {
"fontSize": "var(--wp--preset--font-size--xx-large)"
}
},
"h2": {
"typography": {
"fontSize": "var(--wp--preset--font-size--x-large)"
}
}
}
}
}
}
}
133 changes: 133 additions & 0 deletions inc/ext-vibe-globals.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<?php
/**
* Vibe globals — the read endpoint for this theme's per-vibe GLOBAL theme.json settings.
*
* The payload lives in inc/ext-vibe-globals.json, keyed by the full vibe slug (e.g.
* "linen-1"), and holds the parts of a vibe that are SITE-WIDE rather than per-block:
* settings.layout (contentSize/wideSize), settings.custom scale knobs, and the element
* typography a vibe's heading scale produces.
*
* Why the data is NOT under styles/: WordPress auto-reads that whole tree as selectable
* style variations, so these payloads surfaced in the Site Editor's style browser and —
* because a payload of only styles.elements matches its elements+typography filter exactly
* — in a font picker, offering users a "font" that is really a layout partial. Keeping the
* file outside styles/ makes it inert to WP, so nothing can surface it by accident.
*
* Why an endpoint at all: the file is read directly by anything running on the server (a
* CLI or cron task can just read it), but a browser-side client — the block editor, an
* in-admin AI assistant — cannot touch the filesystem. This route is that window.
*
* Read is public on purpose. The JSON is theme configuration served as a static file at a
* known path under wp-content, so it is already world-readable; requiring a nonce here
* would add no privacy while making the data unreachable to any client that does not hold
* one. Nothing here writes, and nothing here reads request input beyond one slug that is
* only ever used as an array key.
*
* @package Extendable
*/

if ( ! defined( 'ABSPATH' ) ) {
exit;
}

/**
* Absolute path to the vibe-globals manifest, honouring a child theme.
*
* @return string
*/
function extendable_vibe_globals_path() {
return get_theme_file_path( 'inc/ext-vibe-globals.json' );
}

/**
* The decoded manifest, or an empty shape when the file is absent or unreadable.
*
* A theme build that ships no marked vibes has no manifest at all, so absence is a normal
* state and returns the same shape as a present-but-empty one — a caller never has to
* distinguish "no file" from "no vibes".
*
* @return array
*/
function extendable_vibe_globals_data() {
$path = extendable_vibe_globals_path();

if ( ! file_exists( $path ) || ! is_readable( $path ) ) {
return array(
'version' => 1,
'vibes' => array(),
);
}

$decoded = json_decode( (string) file_get_contents( $path ), true );

if ( ! is_array( $decoded ) || ! isset( $decoded['vibes'] ) || ! is_array( $decoded['vibes'] ) ) {
return array(
'version' => 1,
'vibes' => array(),
);
}

return $decoded;
}

/**
* GET /extendable/v1/vibe-globals
*
* Returns the whole manifest, or a single vibe's payload with ?vibe=<slug>. An unknown slug
* is a 404 rather than an empty body, so a client can tell "this theme has no globals for
* that vibe" apart from "that vibe has an empty payload".
*
* @param WP_REST_Request $request Request object.
* @return WP_REST_Response|WP_Error
*/
function extendable_vibe_globals_rest( $request ) {
$data = extendable_vibe_globals_data();
$slug = $request->get_param( 'vibe' );

if ( null === $slug || '' === $slug ) {
return new WP_REST_Response( $data, 200 );
}

if ( ! array_key_exists( $slug, $data['vibes'] ) ) {
return new WP_Error(
'extendable_vibe_globals_not_found',
__( 'No vibe globals for that slug in this theme.', 'extendable' ),
array( 'status' => 404 )
);
}

return new WP_REST_Response(
array(
'version' => $data['version'] ?? 1,
'vibe' => $slug,
'globals' => $data['vibes'][ $slug ],
),
200
);
}

/**
* Register the route.
*
* @return void
*/
function extendable_register_vibe_globals_route() {
register_rest_route(
'extendable/v1',
'/vibe-globals',
array(
'methods' => WP_REST_Server::READABLE,
'callback' => 'extendable_vibe_globals_rest',
'permission_callback' => '__return_true',
'args' => array(
'vibe' => array(
'description' => __( 'Return only this vibe slug, e.g. linen-1.', 'extendable' ),
'type' => 'string',
'required' => false,
'sanitize_callback' => 'sanitize_key',
),
),
)
);
}
add_action( 'rest_api_init', 'extendable_register_vibe_globals_route' );
20 changes: 20 additions & 0 deletions styles/block-presets/button/ext-ghost/preset.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://schemas.wp.org/wp/7.0/theme.json",
"version": 3,
"title": "Ghost",
"slug": "ext-ghost",
"blockTypes": [
"core/button"
],
"styles": {
"color": {
"text": "var:preset|color|foreground",
"background": "transparent"
},
":hover": {
"color": {
"background": "var:preset|color|tertiary"
}
}
}
}
Loading