Custom CSS — Reference
The Custom CSS surface is the design-system styles tier — the place where an operator extends the active theme's tokens with site-wide rules, page-scoped overrides, and the per-record CSS that SG-Builder generates for component styling. It is the canonical writer for everything in the cascade above the theme defaults and below the inline component styles.
This page is a reference for platform engineers and integrators. Custom CSS lives separately from Custom Codes — the latter accepts arbitrary HTML and script for site-wide injection; this surface accepts style rules only and renders into a dedicated style block. The separation is intentional: it lets the platform sanitize and budget styles independently from the script-injection path.
Overview
Custom CSS lives under the Custom module group in SG-Admin, paired with Custom Codes (script and tag injection) and the operator-data siblings Custom Fields, Custom Fonts, and Custom Objects.
The module is paired by convention: one half renders the list, create, edit views; the other half handles writes — save, table refresh, soft delete, restore. The list view groups records by scope and by source — operator-authored rules sit at the top of the list, SG-Builder-generated rules sit underneath in a collapsed group.
A Custom CSS record carries a name, a scope (site-wide or a page allow-list), an enabled flag, a priority within scope, a source flag (operator or sg-builder), and the CSS body itself. On render, the platform composes the active set of records into a single block in the page head, with the cascade ordered theme defaults → site-wide records → page-scoped records → SG-Builder generated rules → inline.
Where it lives in SG-Admin:
- Sidebar: SG-Admin → Custom → CSS
- URL prefix:
/sg-admin/custom-css/ - View templates:
application/views/Admin/CustomCSS/
and CSS expressions that resolve to JavaScript),and renders into a single style block.Custom Codes accepts arbitrary markup and runs without sanitization.Different endpoints,different governance tiers — for design-system work,this surface;for tags and scripts,Custom Codes.SG-Builder integration.SG-Builder writes its per-component CSS through this same surface.When an operator styles a component in the Builder editor,the generated CSS body is persisted as a Custom CSS record withsource=sg-builderand the scope set to the page being built.The Builder editor never writes inline style attributes onto component HTML — every style flows through this layer.
Body-class scope warning.A site's homepage strips the body.page- class on render — the slug class disappears when a page is set as the homepage. To scope a rule to a specific page reliably across both homepage and non-homepage states, use the body.page_id- class. The Builder's generated rules use the ID form automatically;operator-authored rules should follow the same convention.
┌──────────────────────────────────────────────────────────────────────┐│ SG-Admin → Custom → CSS[+Add rule]│├──────────────────────────────────────────────────────────────────────┤│ Operator-authored ││ ───────────────────────────────────────────────────────────────── ││ Name Scope Priority Status ││ Brand button overrides site-wide 10 active ││ Pricing page tweaks page:pricing 50 active ││ Old hero CSS(paused)site-wide 20 paused ││ ││ SG-Builder generated(collapsed)[▶]││ ───────────────────────────────────────────────────────────────── ││ ▶ 42 records — auto-managed by Builder,do not hand-edit ││ ││[⋯ Edit][⋯ Duplicate][⋯ Disable][⋯ Delete]│└──────────────────────────────────────────────────────────────────────┘Actions
The Custom CSS surface exposes the following operations.
List and search
Returns the CSS records visible to the operator,paginated,with name,scope,priority,source,and status columns.The default view groups by source — operator records first,SG-Builder records collapsed.Supports sort,free-text filter(matches name and body),and filter by scope,source,or status.
Create rule
Opens an empty form.Required fields:name,CSS body.Optional:scope(defaults to site-wide),enabled flag(defaults to true),priority(load order within scope,defaults to 50).
Edit rule
Loads an existing record into the same form shape used for create,pre-populated.Submit replaces the stored CSS body and metadata.Operator-authored records are freely editable;SG-Builder-generated records are read-only from this surface(the Builder editor is their canonical writer).
Toggle enabled
Pauses or resumes a rule without deleting it.A paused rule is skipped at render but stays in the list under the paused filter.
Soft delete
Marks the rule as trashed.Trashed rules disappear from the default list and stop applying immediately.SG-Builder-generated rules cannot be soft-deleted from this surface;they delete when the underlying component is deleted in the Builder editor.
Restore
Returns a trashed rule to its prior enabled state.
Permanent delete
Hard-removes a trashed rule.Irreversible.
Data model
A Custom CSS record carries the following fields.
| Field | Type | Notes |
|---|---|---|
id | integer | Primary key.Stable across edits. |
name | string | Operator-readable label.List-view sort key. |
body | text | The CSS source.Sanitized on save. |
scope_mode | enum | site-wideorpage-allowlist. |
scope_page_ids | array | Foreign keys to page records.Empty unlessscope_mode=page-allowlist. |
source | enum | operatororsg-builder.Determines edit access from this surface. |
enabled | boolean | trueapplies at render;falseskips. |
priority | integer | Load order within scope.Lower runs earlier. |
status | enum | activeortrash. |
created_at | timestamp | Immutable. |
updated_at | timestamp | Touched on edit. |
created_by | integer | Foreign key to Users. |
,strips CSS expressions that resolve to JavaScript(theexpression(...)syntax and anyjavascript:URLs inurl(...)),normalizes whitespace,and rejects bodies that exceed the configured per-record byte cap(default 64 KB,configurable in Settings).The cascade.The platform composes active CSS records into a singleblock in the page head,ordered as:
- Theme defaults — typography,color,spacing tokens from the active theme's styles-and-layouts configuration.
- Site-wide operator records — sorted by
priorityascending, thenid. - Page-scoped operator records — only those matching the current page ID, sorted by
priorityascending, thenid. - SG-Builder generated records — auto-managed, in component-tree order.
- Inline overrides — none; the platform does not write inline style attributes.
body.page_id- for page-scoped selectors. The body.page- class disappears when a page is set as the homepage, breaking any selector that depends on it. The platform exposes both classes on non-homepage renders for convenience, but only page_id- is stable across the homepage flip.PAGE RENDER → <style> block composition1. Theme defaults├── typography tokens├── color tokens├── spacing tokens└── breakpoint thresholds↓2. Site-wide operator records(source=operator, scope=site-wide, enabled=true)sorted by priority asc, id asc↓3. Page-scoped operator records(source=operator, scope_page_ids contains current page, enabled=true)sorted by priority asc, id asc↓4. SG-Builder generated records(source=sg-builder, scope matches current page)in component-tree order↓5. Inline overrides─ none ─(Builder never writes style="..." attributes)Final cascade wins by CSS specificity + later-rule precedence.Permissions
Access to the Custom CSS surface is gated at two layers, looser than Custom Codes because the surface accepts styles only.
Layer 1 — admin gate. Standard admin access check at request entry.
Layer 2 — per-action capability. The default capability map ships as:
| Capability | Administrator | Editor | Designer | Viewer |
|---|---|---|---|---|
| List rules | ✔ | ✔ | ✔ | ✔ |
| Create rule | ✔ | ✔ | ✔ | — |
| Edit operator rule | ✔ | ✔ | ✔ | — |
| Edit SG-Builder rule | — | — | — | — |
| Toggle enabled | ✔ | ✔ | ✔ | — |
| Soft delete operator rule | ✔ | ✔ | ✔ | — |
| Soft delete SG-Builder rule | ✔ | — | — | — |
| Restore | ✔ | ✔ | ✔ | — |
| Permanent delete | ✔ | — | — | — |
SG-Builder rule read-only rule.SG-Builder records cannot be edited from this surface by any role.Their canonical writer is the Builder editor;hand-edits here would be silently overwritten on the next component save.The list view marks Builder records visually and disables the edit affordance.
Self-protection.Bodies that exceed the per-record byte cap are rejected with a structured error.Bodies that the sanitizer reduces to empty(everything stripped)are rejected with a structured error rather than persisted as a no-op.
Audit.Every create,edit,toggle,soft delete,restore,and permanent delete emits an Activity Log entry.The diff is full-body — useful for tracking down which rule introduced a regression.
SURFACE ENTRY →/sg-admin/custom-css/...│▼┌──────────────────────┐│ Admin gate │ unauth → reject└──────────┬───────────┘│ authed▼┌──────────────────────┐│ Capability check │ role lacks cap → reject│(per action)│└──────────┬───────────┘│ allowed▼┌──────────────────────┐│ Source check │ source=sg-builder+edit → reject│(edit+delete only)│(Builder is canonical writer)└──────────┬───────────┘│ allowed▼┌──────────────────────┐│ Sanitizer │ strips script,expr,javascript:│ │ byte cap enforced(default 64 KB)└──────────┬───────────┘│ passes▼write executes│▼Activity Log entryRelated references
- Custom Codes — Reference. The companion surface for scripts, tags, and arbitrary HTML injection. Different endpoint, different sanitizer.
- Custom Fonts — Reference. Self-hosted typeface uploads. Surface into the styles-and-layouts font pickers — typically referenced from Custom CSS rules.
- Themes — Reference. Theme defaults form the base of the cascade. A theme switch replaces the base layer; Custom CSS records survive intact.
- Pages — Reference. Page-scoped rules resolve against page IDs. The
body.page_id-selector is the stable scope handle. - SG-Builder — Reference. Builder writes per-component CSS through this surface as
source = sg-builderrecords. - Settings — Reference. Per-record byte cap, sanitizer rule set, Activity Log retention.
/docs/custom-css.