Technical deep-dive into the SYX design system architecture.
┌─────────────────────────────────────────────────────────────┐
│ THEMES │
│ themes/example-01/ themes/example-02/ … │
│ setup.scss + bundle-app/docs/marketing/blog.scss │
├─────────────────────────────────────────────────────────────┤
│ PAGES pages/_landing.scss pages/_docs.scss │
├─────────────────────────────────────────────────────────────┤
│ ORGANISMS header, documentation-layout, … │
├─────────────────────────────────────────────────────────────┤
│ MOLECULES form-field, btn-group, label-group, … │
├─────────────────────────────────────────────────────────────┤
│ ATOMS btn, form, check, radio, switch, link, … │
├─────────────────────────────────────────────────────────────┤
│ UTILITIES display, spacing, text, media, a11y │
│ └─ all public utility classes (.syx-*) │
│ └─ @layer syx.utilities (always wins) │
├─────────────────────────────────────────────────────────────┤
│ BASE reset, elements, theme-helpers │
│ └─ helper-* mixins: theme-aware, generate │
│ .syx-* classes in @layer syx.utilities │
├─────────────────────────────────────────────────────────────┤
│ ABSTRACTS tokens · mixins · functions · maps │
│ (never compiled directly — always @used by other layers) │
└─────────────────────────────────────────────────────────────┘
SYX uses native CSS @layer to manage specificity without !important.
@layer syx.reset, syx.base, syx.tokens, syx.atoms, syx.molecules, syx.organisms, syx.utilities;
| Layer | Content | Wins over |
|---|---|---|
syx.reset |
Browser reset | — |
syx.base |
Element defaults, theme-aware helpers | reset |
syx.tokens |
CSS custom property tokens | base |
syx.atoms |
Atomic components | tokens |
syx.molecules |
Composite components | atoms |
syx.organisms |
Complex UI sections | molecules |
syx.utilities |
All public utility classes (.syx-*) |
everything |
Result: Utility classes always override component styles. No !important needed anywhere.
Note on
helpers/: Thebase/helpers/folder contains theme-aware mixins (helper-fonts,helper-icons,helper-backgrounds, etc.) that receive a$themeparameter and are called from themesetup.scssfiles. They do generate public.syx-*classes (e.g..syx-font-color-primary,.syx-bg-color-primary,.syx-icon--facebook-primary) wrapped in@layer syx.utilities— so they win over all component layers without!important. Public utilities that are not theme-dependent (display, spacing, text, media, a11y) live exclusively inutilities/.
Primitives Theme / Architecture Semantic Tones Component Aliases
────────── ──────────────────── ────────────── ─────────────────
Raw values. Structural config. Contextual feedback. Strict Overrides.
No meaning. General UI feel. Meaningful state. Component-specific.
--primitive-color- --theme-focus-ring- --semantic-tone- --component-btn-
blue-500: … width: 0.2rem info-bg: var( primary-bg:
--primitive-color- var(--semantic-
blue-500) tone-info-bg)
--primitive-{category}-{variant}-{modifier}
--semantic-{purpose}-{variant}-{state}
--component-{name}-{property}-{variant}-{state}
// ✅ Correct — component uses semantic token
.atom-btn--primary {
background: var(--component-btn-primary-bg);
}
// ❌ Wrong — component skips to primitive
.atom-btn--primary {
background: var(--primitive-color-blue-500);
}
15 files in abstracts/mixins/. All mixins are null-safe — passing null skips that property. The library exposes 27 mixins across those 15 files.
mixins/
├── _directional.scss # Shared DRY functions for margin/padding
├── _margin.scss # @include margin(1rem null)
├── _padding.scss # @include padding(null 2rem)
├── _positioning.scss # @include absolute($top: 0, $right: 0)
├── _size.scss # @include size(100%, 48px)
├── _border.scss # @include border(all, 1px, solid, …)
├── _background.scss # @include background-setup(…)
├── _font.scss # @include font-family(…)
├── _media-queries.scss # @include breakpoint(tablet)
├── _helpers.scss # @include transition(), sr-only(), flex-center()…
├── _hide.scss # @include hide-visually()
├── _triangle.scss # @include triangle(…)
├── _behavior.scss # @include behavior-in-ancestor(…)
└── _generate-utilities.scss # @include generate-utility(…)
→ Full reference: abstracts/mixins/README.md
Each theme lives in themes/{name}/ and contains:
themes/example-01/
├── _theme.scss # Primitive overrides (colors, spacing, fonts)
├── setup.scss # Assembles the full theme
├── bundle-app.scss # App context bundle
├── bundle-docs.scss # Documentation context bundle
├── bundle-marketing.scss# Marketing/landing context bundle
└── bundle-blog.scss # Blog/editorial context bundle
Each bundle includes only what that context needs:
// bundle-app.scss — includes everything
@use "setup";
@use "../../atoms/index";
@use "../../molecules/index";
@use "../../organisms/index";
@use "../../utilities/index";
// bundle-marketing.scss — lighter, no complex forms
@use "setup";
@use "../../atoms/index";
@use "../../pages/landing";
See themes/_template/README.md for step-by-step instructions.
Every component lives in one single partial that handles all themes internally. There are no per-theme copies of SCSS files.
organisms/
_header.scss # one file, handles syx + example-02 + coral + forest + midnight
_navbar.scss
…
Inside each mixin, theme variation is handled by three distinct mechanisms depending on the type of difference:
var())For values that all themes have but differently (colors, spacing, icons).
The token is used generically in the partial; each _theme.scss overrides it.
// _header.scss
background-color: var(--component-header-bg); // generic
background-image: var(--component-header-logo-icon); // generic
// themes/example-02/_theme.scss
--component-header-logo-icon: var(--icon-logo-example-02); // theme override
// themes/example-03/_theme.scss
--component-header-logo-icon: var(--icon-logo-coral); // theme override
theme-cfg())For structural/layout differences that need to be resolved at compile time.
Values are stored in abstracts/_theme-config.scss and read with theme-cfg($theme, 'key', $fallback).
// abstracts/_theme-config.scss
$theme-config: (
"example-02": (
header-sidenav-side: right,
header-logo-size: 2.4rem,
),
"coral": (
header-sidenav-side: left,
header-logo-size: 2rem,
),
);
// _header.scss — reads the map
@if theme-cfg($theme, "header-sidenav-side", left) == right {
right: 0;
transform: translateX(100%); // slides in from right
} @else {
left: 0;
transform: translateX(-100%); // slides in from left
}
@if $themeFor one-off rules that only 1–2 themes need. No token or map entry required. Direct override inline in the partial.
// _header.scss
@if $theme == "example-02" {
border-bottom: 1px
solid
var(--semantic-color-border-subtle); // example-02 only
}
@if $theme == "midnight" {
backdrop-filter: blur(8px); // midnight glass effect only
}
| Question | Method |
|---|---|
| Do all themes need this, but with different values? | Method 1 — CSS token |
| Is it layout/structural and reused in multiple places in the mixin? | Method 2 — Sass Map |
| Is it specific to only 1–2 themes and not worth tokenizing? | Method 3 — @if $theme |
$theme-config in abstracts/_theme-config.scssthemes/{name}/_theme.scss@include org-header('mytheme')Atoms — Single-purpose, no dependencies on other components
Molecules — Compose 2+ atoms
Organisms — Compose molecules + atoms, represent UI sections
Pages — Page-specific overrides and layouts
| Layer | Count | Examples |
|---|---|---|
| Atoms | 19 | btn, form, check, radio, switch, link, breadcrumb, pagination, icon, icon-lucide, label, pill, list, table, title, txt, code, feature-icon, stat-counter |
| Molecules | 7 | card, form-field, btn-group, label-group, form-field-set, feature-card, theme-swatch-card |
| Organisms | 8 | header, navbar, content-columns, documentation-layout, home-hero, home-features, home-tokens, home-themes |
| Pages | 3 | home, docs, why-syx |
.syx-block {
}
.syx-block__element {
}
.syx-block--modifier {
}
.syx-block__element--modifier {
}
| Prefix | Meaning | Example |
|---|---|---|
syx- |
SYX utility or component | .atom-btn, .syx-d-flex |
is- |
State | .is-open, .is-active |
js- |
JavaScript hook (no styles) | .js-toggle |
Removed: The
u-prefix (.u-p-sm,.u-text-primary, etc.) has been deprecated and unified undersyx-. All public utility classes use.syx-{property}-{value}exclusively.
| Pattern | Example |
|---|---|
| Atom | atoms/_btn.scss |
| Molecule | molecules/_form-field.scss |
| Organism | organisms/_header.scss |
| Token primitive | abstracts/tokens/primitives/_colors.scss |
| Token semantic | abstracts/tokens/semantic/_colors.scss |
| Token component | abstracts/tokens/components/_btn.scss |
styles-theme-example-01.scss → css/styles-theme-example-01.css
(imports themes/example-01/setup.scss)
styles-core.scss → css/styles-core.css
(neutral template theme, no docs components)
css/prod/styles-core.css (+ PurgeCSS)
themes/example-01/bundle-app.scss → css/theme-01-bundle-app.css
themes/example-01/bundle-docs.scss → css/theme-01-bundle-docs.css
themes/example-01/bundle-marketing.scss→ css/theme-01-bundle-marketing.css
themes/example-01/bundle-blog.scss → css/theme-01-bundle-blog.css
| Decision | Rationale |
|---|---|
Dart Sass @use / @forward |
Explicit imports, no global namespace pollution |
| CSS Custom Properties for tokens | Runtime theming, DevTools visibility |
CSS @layer instead of !important |
Predictable cascade, no specificity wars |
| Null-safe mixins | Shorthand without emitting empty properties |
| Bundle-per-context | Smaller CSS per page type, no unused styles |
| PurgeCSS on production builds | Removes unused selectors, ~20–30% size reduction |
| Bourbon philosophy for mixins | Concise, well-documented, DRY |
| Single-Partial Multi-Theme | One file per component, 3-method pattern inside |