K KodePressdocumentation
Laravel 11 · MySQL 8 Specification Built 2026-10-10

09 — Theme tokens#

The theme is a small set of named values. Blocks and presets reference token names, never raw colours or sizes. One row (theme_settings.tokens) therefore controls the look of the whole site, and a non-technical owner cannot produce an unreadable page by choosing a colour.

9.1 Token document#

{
  "version": 1,
  "color": {
    "primary":        "#1d4ed8",
    "primary_contrast": "#ffffff",
    "secondary":      "#0f766e",
    "accent":         "#f59e0b",
    "text":           "#111827",
    "text_muted":     "#4b5563",
    "heading":        "#0b1220",
    "surface":        "#ffffff",
    "surface_alt":    "#f3f4f6",
    "surface_dark":   "#0b1220",
    "border":         "#e5e7eb",
    "success":        "#15803d",
    "warning":        "#b45309",
    "danger":         "#b91c1c"
  },
  "font": {
    "body":        { "family": "Noto Sans Bengali", "weights": [400, 500, 700] },
    "heading":     { "family": "Noto Serif Bengali", "weights": [600, 700] },
    "body_latin":  { "family": "Inter", "weights": [400, 500, 700] },
    "base_size":   16,
    "scale":       1.25,
    "line_height": 1.7,
    "heading_line_height": 1.25,
    "letter_spacing": 0
  },
  "space": { "unit": 4, "section_small": 32, "section_medium": 64, "section_large": 96, "section_xlarge": 128, "gap_sm": 8, "gap_md": 16, "gap_lg": 32 },
  "radius": { "none": 0, "sm": 4, "md": 8, "lg": 16, "full": 999, "default": "md" },
  "button": { "style": "solid", "radius": "md", "size": "md", "weight": 600, "uppercase": false, "shadow": "none" },
  "layout": { "container": 1200, "wide": 1440, "content": 760, "breakpoint_tablet": 1024, "breakpoint_mobile": 768 },
  "shadow": { "sm": "0 1px 2px rgba(0,0,0,.06)", "md": "0 4px 12px rgba(0,0,0,.08)", "lg": "0 12px 32px rgba(0,0,0,.12)" },
  "image": { "default_ratio": "16/9", "rounded": true }
}

Only token names appear in block data. A section with background: "primary" means var(--kp-color-primary); it does not store #1d4ed8. Re-theming is therefore instant and total.

9.2 Design screen#

Design -> Theme is organised the way an owner thinks, not the way the JSON is shaped:

Group Controls
Presets 4 ready themes; picking one replaces all tokens after a confirm
Colours primary, secondary, accent, then text/surface/border under More colours; each swatch shows a live contrast badge
Typography body font, heading font, base size slider, scale, line height; Bengali fonts listed first with a Bengali sample
Buttons style (solid / outline / soft / link), radius, size, weight, uppercase
Spacing section padding scale (compact / normal / roomy) mapping to the space tokens
Layout container width, content width, breakpoints (Advanced)
Custom CSS Admin only, under Advanced, with a warning that it is not covered by support

Every change previews live against a sample page in a split view, and Save is explicit. A Discard changes button restores the saved tokens.

Contrast guard#

When a chosen colour pair falls below WCAG AA (4.5:1 for body text, 3:1 for large text and UI), the field shows a warning and suggests the nearest passing shade with a one-click Use this button. It warns; it does not block — an owner may have a brand requirement. The warning is recorded so Dashboard -> Needs attention can list accessibility issues.

9.3 Shipped presets#

Preset Character Primary Heading font
default neutral, corporate, blue #1d4ed8 Noto Serif Bengali
warm editorial, news-like, amber on cream #b45309 Noto Serif Bengali
fresh clinic / education, green, rounded #15803d Noto Sans Bengali
dark dark surfaces, high contrast, tech #38bdf8 Inter + Noto Sans Bengali

Each preset ships with matching button, radius and spacing choices, so switching preset is a real visual change rather than a palette swap. Presets are stored as seeder data, not as rows a user can corrupt.

9.4 Compilation to CSS#

TokenCompiler turns the token document into one stylesheet:

theme_settings.tokens
   -> render resources/css/theme.css.blade.php with the tokens
   -> minify
   -> md5 -> theme_settings.css_hash
   -> write storage/app/public/theme/theme-{hash}.css
   -> pages link /storage/theme/theme-{hash}.css

Generated structure:

:root{
  --kp-color-primary:#1d4ed8; --kp-color-primary-contrast:#fff; /* ... */
  --kp-font-body:"Noto Sans Bengali",system-ui,sans-serif;
  --kp-space-section-md:64px; --kp-radius-md:8px; --kp-container:1200px;
}
/* element defaults */
body{font-family:var(--kp-font-body);color:var(--kp-color-text);line-height:var(--kp-font-line-height)}
h1,h2,h3,h4{font-family:var(--kp-font-heading);color:var(--kp-color-heading)}
/* layout primitives */
.kp-container{max-width:var(--kp-container);margin-inline:auto;padding-inline:16px}
.kp-section{padding-block:var(--kp-space-section-md)}
.kp-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--kp-gap-md)}
.kp-col-7{grid-column:span 7}
/* component classes used by block views */
.kp-btn{...}.kp-btn--primary{...}.kp-heading{...}.kp-card{...}
/* responsive */
@media (max-width:1024px){ /* tablet spans */ }
@media (max-width:768px){ .kp-grid>*{grid-column:1/-1} /* stacking */ }
/* appended: block.css files of blocks in use, and custom_css last */

Utility classes are hand-written in theme.css.blade.php, not generated from a utility framework. There is no Tailwind in the public output: the public CSS must be small, stable and fully determined by tokens. The admin UI does use a compiled utility stylesheet, built by Vite and committed.

Budget: the generated public stylesheet stays under 40 KB uncompressed. A test asserts it.

9.5 Fonts#

9.6 Dark mode#

Out of scope as a visitor toggle in Phase 1–4. The dark preset is a dark design, not a mode. The token document is shaped so a future tokens.dark block can be added and emitted under @media (prefers-color-scheme: dark) without touching block views — because every block view reads var(--kp-*) and nothing else. This is the reason block views are forbidden from inline colours.

9.7 Tests for this document#

Test Asserts
token_change_regenerates_css_with_new_hash and the page links the new file
token_change_clears_all_page_cache both of two cached pages are gone
preset_switch_replaces_tokens and keeps page content untouched
generated_css_under_budget < 40 KB uncompressed for the default preset
block_views_contain_no_hex_colours a grep-style test over app/Blocks/*/view.blade.php
contrast_warning_appears_for_low_contrast_pair warning, not a block
missing_css_file_is_recompiled_on_request one compile, then served
bengali_font_face_has_unicode_range and font-display: swap

KodePress documentation · generated from the Markdown sources by tools/build-docs-site.py · internal preview, not indexed.