Skip to content

feat: C3 · Global styles model, migration, API, schema pin - #364

Merged
ViewFromTheBox merged 3 commits into
release/1.0from
feature/359-c3-global-styles-backend
Apr 23, 2026
Merged

ViewFromTheBox merged 3 commits into
release/1.0from
feature/359-c3-global-styles-backend

Conversation

@ViewFromTheBox

@ViewFromTheBox ViewFromTheBox commented Apr 23, 2026 •

Copy link
Copy Markdown
Member

Description

Lands the server-side backing for the globalStyles singleton the B1 core-data shim (#353) addresses at /visual-editor/api/global-styles. The record is a theme.json-shaped { version, settings, styles } blob, scoped by theme to support future multi-theme hosting. Four endpoints — lookup, show, update, base — unblock the D3 site-editor style UI and E1 front-end CSS emission.

Closes: #359

Type of Change

  • New feature (adds new functionality)

Related Issue

Issue: #359

Motivation and Context

The B1 core-data shim dispatches three endpoint patterns against globalStyles — lookup, CRUD on the singleton, and a separate base endpoint for the theme defaults the site-editor style UI diffs against. Phase C is the backend work that makes each entity's endpoints return real payloads; C3 is the global-styles slice.

Per §4.2 of the V1 plan doc, the theme.json schema is pinned to a specific version so upstream @wordpress/* bumps do not silently drift the schema. The pin is recorded as configurable data (artisanpack.visual-editor.global_styles.schema_version, default 3) and enforced by the PUT form-request, not a hard-coded constant.

Changes Made

  • Migration visual_editor_global_styles (version, settings, styles, theme unique).
  • VisualEditorGlobalStyles model with a resolveSingleton($theme, $defaults) helper that lazily seeds the singleton row on first lookup — no manual seeder needed.
  • GlobalStylesController with lookup, show, update, base actions; routes declared so static segments (lookup, base) win over the wildcard model-binding segment.
  • UpdateGlobalStylesRequest validates: pinned-version match, required settings/styles, palette entry shape, unique preset slugs across color/typography families.
  • Controller persists the full settings/styles blob verbatim via $request->input() — using validated() would silently strip extensible theme.json keys (layout, spacing, etc.) that the form-request doesn't enforce shape on.
  • Default base payload bundled at resources/theme-json/default-base.php; host apps can override via artisanpack.visual-editor.global_styles.base_path.
  • VisualEditorGlobalStylesPolicy gates access (auth required on every endpoint).
  • docs/global-styles.md documents the pinned v3 schema, what "compatible" means, and the steps for a future schema bump.

How Has This Been Tested?

Testing Environment:

  • Operating System: macOS (Darwin 25.3.0)
  • PHP Version: 8.4.3
  • Project Version: release/1.0

Tests Performed:

  1. Full Pest suite: ./vendor/bin/pest --compact → 271 passed (822 assertions).
  2. New test file: ./vendor/bin/pest tests/Feature/VisualEditor/GlobalStylesControllerTest.php → 18 passed (72 assertions).
  3. Manual verification by the issue author in the dev app (backend-only work — no visual surface yet; D3 wires the UI).

Accessibility Tests Run

  • Keyboard navigation tested
  • Screen reader tested
  • Color contrast verified
  • ARIA labels checked

Details: Not applicable — backend REST endpoints, no UI surface. D3 (site-editor global-styles UI) will cover accessibility testing for the consumer of these endpoints.

Tests Added

  • Unit tests added/updated
  • Integration tests added/updated
  • All tests passing

Test details: 18 new Pest feature tests covering:

  • lookup auth gate, idempotency, theme scoping
  • show auth gate, full record shape, 404
  • update auth gate, happy path, 422 on schema-version mismatch, missing version, missing settings/styles, duplicate palette slugs, malformed palette entry
  • base auth gate, shape assertion, base_path config override
  • B2 global-styles fixture (tests/Fixtures/sample-content/global-styles/default.json) round-trips through PUT + GET without reshaping

Documentation

  • Inline code documentation added
  • API documentation updated

Documentation details: New docs/global-styles.md records the pinned schema version, the relationship to the vendored @wordpress/* packages, the validation constraints, the multi-theme scoping model, and an explicit upgrade checklist for future schema bumps. File-level and method-level PHPDoc blocks follow the same convention as the C1 / C2 siblings.

Screenshots

Not applicable — backend-only work.

Pre-Submission Checklist

  • Followed contributing guidelines
  • Checked for other open PRs for same update
  • Code passes all tests
  • Code has been linted (house style hand-verified against C2 patterns; package has no local linter wired up)
  • Accessibility tests completed (n/a, backend only)
  • Code follows project style guide
  • Self-review completed
  • Comments added for complex code
  • No new warnings generated

Summary by CodeRabbit

  • New Features

    • Global theme styles management with REST endpoints for lookup, retrieval, update, and base defaults; per-theme singleton persistence and pinned schema versioning.
    • Auth-guarded APIs and standardized JSON responses for global styles; configurable base payload source.
  • Documentation

    • New guide describing payload shape, endpoints, validation rules, theme scoping, and schema-bump process.
    • Added default base theme.json-styled payload.
  • Tests

    • Comprehensive feature tests covering auth, lookup/show/update flows, validation, base-path override, and end-to-end persistence.

Lands the server-side backing for the globalStyles singleton the
B1 core-data shim addresses at /visual-editor/api/global-styles:

- visual_editor_global_styles table (version, settings, styles,
  theme — unique) + VisualEditorGlobalStyles model with a
  resolveSingleton() helper that lazily seeds the row on first
  lookup.
- Four endpoints — lookup, show, update, base — mounted under
  /visual-editor/api/global-styles behind the auth middleware
  stack. Routes are declared in the order required for the
  static segments to win over wildcard model binding.
- UpdateGlobalStylesRequest validates against the pinned
  theme.json schema version (configurable via
  artisanpack.visual-editor.global_styles.schema_version),
  required settings / styles shape, palette entry fields, and
  unique preset slugs across color / typography families. The
  controller persists the full settings / styles blob verbatim
  so extensible theme.json keys (layout, spacing, etc.) are not
  silently dropped by validated().
- Default base payload bundled at
  resources/theme-json/default-base.php; host apps can swap it
  via a config path override.
- VisualEditorGlobalStylesPolicy gates access (auth required).
- docs/global-styles.md documents the pinned schema version
  (3), what "compatible" means, and the steps we take when
  upstream theme.json bumps.
- 18 Pest feature tests cover lookup idempotency + theme
  scoping, show / 404, update happy path + 422s (missing
  version, mismatched version, missing settings / styles,
  duplicate palette slugs, malformed palette entry), base
  payload shape, base_path config override, and B2 global-
  styles fixture round-trip. Auth gating verified on every
  endpoint.

Closes #359
@coderabbitai

coderabbitai Bot commented Apr 23, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d69cecd2-7217-4536-bb4c-c6c7c0ea8a30

📥 Commits

Reviewing files that changed from the base of the PR and between 1bd03b6 and efd9ccd.

📒 Files selected for processing (1)
  • src/Models/VisualEditorGlobalStyles.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Models/VisualEditorGlobalStyles.php

📝 Walkthrough

Walkthrough

Adds server-side support for a singleton, theme.json-shaped globalStyles entity: new config, migration + Eloquent model, REST endpoints (lookup, show, update, base), request validation pinned to a schema version, default base payload, policy registration, docs, and feature tests.

Changes

Cohort / File(s) Summary
Configuration & Provider
config/visual-editor.php, src/VisualEditorServiceProvider.php
Introduce global_styles config (theme, schema_version, base_path) and register VisualEditorGlobalStylesPolicy.
Database & Model
database/migrations/2026_04_23_200000_create_visual_editor_global_styles_table.php, src/Models/VisualEditorGlobalStyles.php
Create visual_editor_global_styles table (unique theme, version int, settings/styles JSON, timestamps) and model with resolveSingleton() for lazy per-theme creation.
Routes & Controller
routes/api.php, src/Http/Controllers/GlobalStylesController.php
Mount /visual-editor/api/global-styles endpoints (lookup, base, show, update) and implement controller actions to resolve, return, update, and load base payload (config override supported).
Validation
src/Http/Requests/UpdateGlobalStylesRequest.php
New FormRequest enforcing pinned version, required settings/styles arrays, preset entry shapes, and uniqueness of preset slug values via a custom closure rule; custom message for version mismatch.
Authorization
src/Policies/VisualEditorGlobalStylesPolicy.php
Add policy gating lookup, base, show, and update to authenticated users and register it in service provider.
Serialization & Base Payload
src/Http/Resources/GlobalStylesResource.php, resources/theme-json/default-base.php
Add JSON resource formatting (id, version, settings, styles) and bundle a version-3 default base payload as a PHP array.
Documentation
docs/global-styles.md
Document pinned schema version (v3), REST contract (lookup, show, update, base), validation rules, multi-theme scoping, and upgrade guidance.
Tests
tests/Feature/VisualEditor/GlobalStylesControllerTest.php
Add feature tests covering auth, lookup idempotency, show/update behavior and validation (422 cases), base payload and base_path override, and end-to-end fixture round-trip.

Sequence Diagram

sequenceDiagram
    actor Client
    participant Controller as GlobalStylesController
    participant Validator as UpdateGlobalStylesRequest
    participant Model as VisualEditorGlobalStyles
    participant DB as Database
    participant Resource as GlobalStylesResource

    Client->>Controller: PUT /visual-editor/api/global-styles/{id}
    Controller->>Validator: validate($request)
    Validator->>Validator: ensure version == config schema_version
    Validator->>Validator: ensure settings/styles arrays and unique preset slugs
    alt validation fails
        Validator-->>Client: 422 Unprocessable Entity (errors)
    else validation passes
        Validator-->>Controller: validated data
        Controller->>Model: set version, settings, styles
        Model->>DB: save()
        DB-->>Model: persisted record
        Controller->>Resource: new GlobalStylesResource(model)
        Resource-->>Client: 200 OK (id, version, settings, styles)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰
I nibbled through migrations, hopped past a test,
Pinned to version three, the defaults rest,
One singleton burrowed per theme so neat,
Settings and styles safe, tidy and sweet,
A rabbit's thump — the global styles are complete!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: C3 · Global styles model, migration, API, schema pin' directly summarizes the main changes: adding the model, migration, API endpoints, and schema version pinning for global styles.
Linked Issues check ✅ Passed All in-scope requirements from #359 are met: model and migration created, all four endpoints implemented, form-request validation enforces schema version and preset rules, base payload bundled with override support, documentation provided, comprehensive tests added, and policy gates access.
Out of Scope Changes check ✅ Passed All changes align with #359 scope: no CSS emission, UI, style variations, or revision history code added. Changes are focused on model, migration, API, validation, and tests as specified.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/359-c3-global-styles-backend

Comment @coderabbitai help to get the list of available commands and usage tips.

@ViewFromTheBox ViewFromTheBox moved this from Backlog to In Review in ArtisanPack UI Overview Apr 23, 2026
@ViewFromTheBox ViewFromTheBox linked an issue Apr 23, 2026 that may be closed by this pull request
10 tasks
@ViewFromTheBox
ViewFromTheBox marked this pull request as ready for review April 23, 2026 18:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@resources/theme-json/default-base.php`:
- Around line 7-8: The header comment references the wrong config key name;
update the comment that mentions `artisanpack.visual-editor.global_styles.base`
to the correct key `artisanpack.visual-editor.global_styles.base_path` so
host-app overrides and docs reference the actual config entry used by the code
(look for the header/comment block in resources/theme-json/default-base.php and
replace the mistaken symbol).

In `@src/Http/Controllers/GlobalStylesController.php`:
- Around line 56-59: The singleton resolution in
VisualEditorGlobalStyles::resolveSingleton (used by lookup()) is vulnerable to
race conditions because it uses firstOrCreate() on the unique theme key; wrap
the resolution in a DB transaction and perform a SELECT ... FOR UPDATE (e.g.,
use DB::transaction + lockForUpdate() on a query for the theme) before calling
firstOrCreate(), or replace the create path with a single atomic upsert call
supported by your driver so that concurrent cold-start requests cannot both
attempt to insert the same theme for activeTheme() / basePayload().

In `@src/Http/Requests/UpdateGlobalStylesRequest.php`:
- Around line 55-56: The current validation for settings.typography.fontFamilies
and .fontSizes only enforces array + unique slugs, so incomplete items slip
through; update the request validation in UpdateGlobalStylesRequest.php to
require each array item contains the expected keys (e.g. for fontFamilies
require each array.*.name and array.*.fontFamily as required strings; for
fontSizes require array.*.name and array.*.size as required numbers/strings as
appropriate) in addition to keeping the existing
'settings.typography.fontFamilies'/'settings.typography.fontSizes' array rules
and the $this->uniqueSlugRule('fontFamilies'|'fontSizes') checks so entries must
be fully shaped like theme.json presets.

In `@tests/Feature/VisualEditor/GlobalStylesControllerTest.php`:
- Around line 258-270: The temp file is leaked because the code appends ".php"
to the path returned by tempnam() and only unlinks the appended path; change the
creation to keep tempnam()'s returned path (i.e. set $override =
tempnam(sys_get_temp_dir(), 'base-') without appending '.php'), write the PHP
return contents into that same $override with file_put_contents, leave the
existing try/finally that calls `@unlink`($override), and keep the
config()->set('artisanpack.visual-editor.global_styles.base_path', $override)
and the getJson('/visual-editor/api/global-styles/base') assertions as-is.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6c78862e-c194-4e5e-8933-56d42843a6ca

📥 Commits

Reviewing files that changed from the base of the PR and between 1adcaf0 and 97dce68.

📒 Files selected for processing (12)
  • config/visual-editor.php
  • database/migrations/2026_04_23_200000_create_visual_editor_global_styles_table.php
  • docs/global-styles.md
  • resources/theme-json/default-base.php
  • routes/api.php
  • src/Http/Controllers/GlobalStylesController.php
  • src/Http/Requests/UpdateGlobalStylesRequest.php
  • src/Http/Resources/GlobalStylesResource.php
  • src/Models/VisualEditorGlobalStyles.php
  • src/Policies/VisualEditorGlobalStylesPolicy.php
  • src/VisualEditorServiceProvider.php
  • tests/Feature/VisualEditor/GlobalStylesControllerTest.php

Comment thread resources/theme-json/default-base.php Outdated
Comment on lines +56 to +59
$record = VisualEditorGlobalStyles::resolveSingleton(
$this->activeTheme(),
$this->basePayload()
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

model="$(fd -a 'VisualEditorGlobalStyles.php' src | head -n1)"
migration="$(fd -a 'visual_editor_global_styles' database | head -n1)"

test -n "$model"
test -n "$migration"

echo "== resolveSingleton() =="
nl -ba "$model" | sed -n '70,90p'

echo
echo "== visual_editor_global_styles migration =="
nl -ba "$migration" | sed -n '1,200p'

Repository: ArtisanPack-UI/visual-editor

Length of output: 157


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the model file
model=$(fd -a 'VisualEditorGlobalStyles.php' src 2>/dev/null | head -n1)
echo "Model file: $model"

# Find the migration file
migration=$(fd -a 'visual_editor_global_styles' database 2>/dev/null | head -n1)
echo "Migration file: $migration"

if [ -z "$model" ] || [ -z "$migration" ]; then
  echo "Files not found, searching more broadly..."
  echo "=== Searching for VisualEditorGlobalStyles ==="
  find . -name '*VisualEditorGlobalStyles*' -type f 2>/dev/null | head -20
  
  echo "=== Searching for visual_editor_global_styles ==="
  find . -name '*visual_editor_global_styles*' -type f 2>/dev/null | head -20
fi

# Also check the GlobalStylesController
echo "=== Searching for GlobalStylesController ==="
find . -name '*GlobalStylesController*' -type f 2>/dev/null

Repository: ArtisanPack-UI/visual-editor

Length of output: 415


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== GlobalStylesController.php (lines 40-75) ==="
cat -n ./src/Http/Controllers/GlobalStylesController.php | head -75 | tail -36

echo ""
echo "=== VisualEditorGlobalStyles.php (entire file) ==="
wc -l ./src/Models/VisualEditorGlobalStyles.php

echo ""
echo "=== VisualEditorGlobalStyles.php content ==="
cat -n ./src/Models/VisualEditorGlobalStyles.php

echo ""
echo "=== Migration file content ==="
cat -n ./database/migrations/2026_04_23_200000_create_visual_editor_global_styles_table.php

Repository: ArtisanPack-UI/visual-editor

Length of output: 7081


Make singleton resolution race-safe.

lookup() currently relies on resolveSingleton(), which uses firstOrCreate() on a unique theme key. Two cold-start requests can both miss the row and one will 500 on the unique index instead of returning the same singleton.

Consider wrapping the resolution in database-level locking (e.g., firstOrCreate() within a transaction with lockForUpdate()) or using an upsert operation if supported by your database driver, to ensure the operation is atomic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Http/Controllers/GlobalStylesController.php` around lines 56 - 59, The
singleton resolution in VisualEditorGlobalStyles::resolveSingleton (used by
lookup()) is vulnerable to race conditions because it uses firstOrCreate() on
the unique theme key; wrap the resolution in a DB transaction and perform a
SELECT ... FOR UPDATE (e.g., use DB::transaction + lockForUpdate() on a query
for the theme) before calling firstOrCreate(), or replace the create path with a
single atomic upsert call supported by your driver so that concurrent cold-start
requests cannot both attempt to insert the same theme for activeTheme() /
basePayload().

Comment thread src/Http/Requests/UpdateGlobalStylesRequest.php Outdated
Comment thread tests/Feature/VisualEditor/GlobalStylesControllerTest.php Outdated
- Correct the header comment in resources/theme-json/default-base.php
  to reference the actual config key — artisanpack.visual-editor.
  global_styles.base_path — that the controller honors for base-payload
  overrides. The earlier `.base` was a stale symbol.
- Tighten UpdateGlobalStylesRequest so typography preset entries must
  carry their full shape: fontFamilies entries now require slug, name,
  and fontFamily; fontSizes entries require slug, name, and size. The
  palette already enforced per-item required keys, and the typography
  presets are addressed the same way (CSS variables keyed by slug), so
  partial entries would have silently produced broken var(--…) tokens.
- Plug a temp-file leak in the base_path override test. tempnam()
  creates the file at the path it returns; appending ".php" before
  writing meant the finally block unlinked a sibling file and orphaned
  the tempnam-created stub. Dropped the extension append — require
  does not care.
- Document that the cold-start race on VisualEditorGlobalStyles::
  resolveSingleton() is already covered by Laravel's race-safe
  firstOrCreate (which catches UniqueConstraintViolationException and
  re-queries) plus the unique theme index, so future reviewers do not
  re-raise the concern.

Two new feature tests exercise the fontFamilies / fontSizes malformed-
entry cases. Full suite: 273 passed (828 assertions).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/Http/Requests/UpdateGlobalStylesRequest.php (1)

95-95: Silence the unused-parameter warning in the closure signature.

$attribute is required by the rule signature but currently unused; renaming to $_attribute keeps intent clear and avoids PHPMD noise.

Suggested tweak
-		return function ( string $attribute, mixed $value, Closure $fail ) use ( $label ): void {
+		return function ( string $_attribute, mixed $value, Closure $fail ) use ( $label ): void {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Http/Requests/UpdateGlobalStylesRequest.php` at line 95, The closure
returned in UpdateGlobalStylesRequest:: (the function signature starting
"function ( string $attribute, mixed $value, Closure $fail ) use ( $label )")
should rename the unused $attribute parameter to $_attribute to silence
PHPMD/unused-parameter warnings; update the closure signature to "function (
string $_attribute, mixed $value, Closure $fail ) use ( $label )" and leave the
rest of the closure body unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Models/VisualEditorGlobalStyles.php`:
- Line 86: Replace the hardcoded fallback version on the 'version' assignment in
VisualEditorGlobalStyles (the line "'version'  => isset( $defaults['version'] )
? (int) $defaults['version'] : 3") so it reads the canonical schema version from
configuration; use
config('artisanpack.visual-editor.global_styles.schema_version') (cast to int)
as the fallback (optionally with a numeric default in config(...) to preserve
safety) instead of the literal 3.

---

Nitpick comments:
In `@src/Http/Requests/UpdateGlobalStylesRequest.php`:
- Line 95: The closure returned in UpdateGlobalStylesRequest:: (the function
signature starting "function ( string $attribute, mixed $value, Closure $fail )
use ( $label )") should rename the unused $attribute parameter to $_attribute to
silence PHPMD/unused-parameter warnings; update the closure signature to
"function ( string $_attribute, mixed $value, Closure $fail ) use ( $label )"
and leave the rest of the closure body unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 24b8f598-0ca8-4f29-b68e-668fb1b29f4d

📥 Commits

Reviewing files that changed from the base of the PR and between 97dce68 and 1bd03b6.

📒 Files selected for processing (4)
  • resources/theme-json/default-base.php
  • src/Http/Requests/UpdateGlobalStylesRequest.php
  • src/Models/VisualEditorGlobalStyles.php
  • tests/Feature/VisualEditor/GlobalStylesControllerTest.php
✅ Files skipped from review due to trivial changes (1)
  • resources/theme-json/default-base.php
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/Feature/VisualEditor/GlobalStylesControllerTest.php

Comment thread src/Models/VisualEditorGlobalStyles.php Outdated
The singleton auto-create path was falling back to a literal `3`
when the caller did not supply a `version` in the seed payload,
while the controller and form-request already read
artisanpack.visual-editor.global_styles.schema_version from config.
Unified the three callers on the same config key so the pinned
version lives in exactly one place — the config default still
reads 3, so behavior is unchanged for default installs.
@ViewFromTheBox
ViewFromTheBox merged commit 3b6ac52 into release/1.0 Apr 23, 2026
1 check passed
@github-project-automation github-project-automation Bot moved this from In Review to Done in ArtisanPack UI Overview Apr 23, 2026
@ViewFromTheBox
ViewFromTheBox deleted the feature/359-c3-global-styles-backend branch April 23, 2026 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

C3 · Global styles: theme.json schema, model, migration, API

1 participant