@syncedco/flow 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +2 -1
- package/base.css +2 -1
- package/bin/synced-flow.mjs +30 -12
- package/components.css +18 -2
- package/docs/audit-2026-06.md +113 -0
- package/docs/base-styling.md +5 -0
- package/docs/config-reference.md +27 -0
- package/docs/system-primitives.md +2 -1
- package/docs/tokens.md +8 -1
- package/docs/why-synced-flow.md +1 -1
- package/examples/astro/src/styles/synced-flow.generated.css +14 -0
- package/examples/next/app/synced-flow.generated.css +14 -0
- package/examples/plain-html/synced-flow.generated.css +14 -0
- package/examples/vite/src/synced-flow.generated.css +14 -0
- package/examples/wordpress/assets/css/synced-flow.css +400 -4
- package/layout.css +5 -4
- package/package.json +3 -2
- package/reset.css +1 -1
- package/styles.css +54 -9
- package/tokens.css +27 -0
- package/utilities.css +1 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.2 - 2026-06-15
|
|
4
|
+
|
|
5
|
+
- Add an explicit accessible root typography baseline with `html { font-size: 100%; }`.
|
|
6
|
+
- Add `--sf-text-base` and generated `--text-base` aliases for fluid body text.
|
|
7
|
+
- Use the new fluid body text token in shipped and generated base CSS.
|
|
8
|
+
- Convert CLI-generated utility lengths for blur, rings, outlines, and thicker borders from static `px` to `rem`.
|
|
9
|
+
- Extend guardrails and tests so shipped generated examples reject raw pixels outside allowed hairline, visually-hidden, and forced-colors exceptions.
|
|
10
|
+
|
|
11
|
+
## 0.3.1 - 2026-06-13
|
|
12
|
+
|
|
13
|
+
- Fix mobile app drawer scrolling by giving the drawer sidebar a viewport block size.
|
|
14
|
+
- Vertically center `.sf-tab` contents.
|
|
15
|
+
- Improve built-in dark theme token coverage for primary, state, soft, ring, and glow tokens.
|
|
16
|
+
- Make `.sf-filter-bar` alignment token-configurable with a centered default.
|
|
17
|
+
- Add a status-chip dot opt-out with `data-dot="false"` and `sf-status--label`.
|
|
18
|
+
- Add reduced-motion safeguards for hover lift and marquee animation.
|
|
19
|
+
- Add public `--sf-z-*` stacking tokens for header, backdrop, drawer, overlay, toast, sticky, and skip-link layers.
|
|
20
|
+
- Reframe positioning copy from "AI-native" to "AI-friendly".
|
|
21
|
+
- Document the global theme config scope and the project-CSS pattern for advanced theme matrices.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Synced Flow
|
|
2
2
|
|
|
3
|
-
AI-
|
|
3
|
+
AI-friendly fluid CSS design system for brand-consistent websites, agency builds,
|
|
4
4
|
and modern frontend projects.
|
|
5
5
|
|
|
6
6
|
Synced Flow gives developers and AI coding agents a shared styling contract:
|
|
@@ -58,6 +58,7 @@ documentation, and recipes.
|
|
|
58
58
|
Useful project files:
|
|
59
59
|
|
|
60
60
|
- [Contributing](CONTRIBUTING.md)
|
|
61
|
+
- [Changelog](CHANGELOG.md)
|
|
61
62
|
- [Security policy](SECURITY.md)
|
|
62
63
|
- [Code of conduct](CODE_OF_CONDUCT.md)
|
|
63
64
|
- [Support](SUPPORT.md)
|
package/base.css
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
html {
|
|
5
5
|
background: var(--sf-colour-background);
|
|
6
6
|
color: var(--sf-colour-foreground);
|
|
7
|
+
font-size: 100%;
|
|
7
8
|
scroll-behavior: smooth;
|
|
8
9
|
}
|
|
9
10
|
|
|
@@ -11,7 +12,7 @@
|
|
|
11
12
|
background: var(--sf-colour-background);
|
|
12
13
|
color: var(--sf-colour-foreground);
|
|
13
14
|
font-family: var(--sf-font-sans);
|
|
14
|
-
font-size: var(--sf-
|
|
15
|
+
font-size: var(--sf-text-base);
|
|
15
16
|
line-height: var(--sf-line-height-body);
|
|
16
17
|
-webkit-font-smoothing: antialiased;
|
|
17
18
|
text-rendering: optimizeLegibility;
|
package/bin/synced-flow.mjs
CHANGED
|
@@ -2980,6 +2980,19 @@ function pxToRem(value) {
|
|
|
2980
2980
|
})
|
|
2981
2981
|
}
|
|
2982
2982
|
|
|
2983
|
+
function pixelNumberToRem(value) {
|
|
2984
|
+
const number = Number(value)
|
|
2985
|
+
if (Number.isNaN(number)) return null
|
|
2986
|
+
if (number === 0) return '0'
|
|
2987
|
+
return `${round(number / 16)}rem`
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
function borderWidthUnit(value) {
|
|
2991
|
+
if (value === '0') return '0'
|
|
2992
|
+
if (value === '1') return '1px'
|
|
2993
|
+
return pixelNumberToRem(value)
|
|
2994
|
+
}
|
|
2995
|
+
|
|
2983
2996
|
function arbitraryValue(value) {
|
|
2984
2997
|
return pxToRem(value.replace(/_/g, ' '))
|
|
2985
2998
|
}
|
|
@@ -3650,7 +3663,7 @@ function colourUtility(base) {
|
|
|
3650
3663
|
function borderUtility(base) {
|
|
3651
3664
|
if (base === 'border') return [['border-width', '1px']]
|
|
3652
3665
|
if (base === 'border-0') return [['border-width', '0']]
|
|
3653
|
-
if (base === 'border-2') return [['border-width', '
|
|
3666
|
+
if (base === 'border-2') return [['border-width', '0.125rem']]
|
|
3654
3667
|
if (base === 'border-t') return [['border-top-width', '1px']]
|
|
3655
3668
|
if (base === 'border-r') return [['border-right-width', '1px']]
|
|
3656
3669
|
if (base === 'border-b') return [['border-bottom-width', '1px']]
|
|
@@ -3660,7 +3673,7 @@ function borderUtility(base) {
|
|
|
3660
3673
|
const sideWidth = base.match(/^border-(t|r|b|l)-(\d+)$/)
|
|
3661
3674
|
if (sideWidth) {
|
|
3662
3675
|
const side = { t: 'top', r: 'right', b: 'bottom', l: 'left' }[sideWidth[1]]
|
|
3663
|
-
return [[`border-${side}-width`,
|
|
3676
|
+
return [[`border-${side}-width`, borderWidthUnit(sideWidth[2])]]
|
|
3664
3677
|
}
|
|
3665
3678
|
if (base === 'divide-y') return [['--sf-divide-y', '1px']]
|
|
3666
3679
|
if (base === 'divide-x') return [['--sf-divide-x', '1px']]
|
|
@@ -3724,26 +3737,26 @@ function effectUtility(base) {
|
|
|
3724
3737
|
if (match) return [['opacity', String(Number(match[1]) / 100)]]
|
|
3725
3738
|
match = base.match(/^blur-(none|sm|md|lg|xl|2xl|3xl)$/)
|
|
3726
3739
|
if (match) {
|
|
3727
|
-
const map = { none: '0', sm: '
|
|
3740
|
+
const map = { none: '0', sm: '0.25rem', md: '0.75rem', lg: '1rem', xl: '1.5rem', '2xl': '2.5rem', '3xl': '4rem' }
|
|
3728
3741
|
return [['filter', `blur(${map[match[1]]})`]]
|
|
3729
3742
|
}
|
|
3730
3743
|
match = base.match(/^backdrop-blur(?:-(none|sm|md|lg|xl))?$/)
|
|
3731
3744
|
if (match) {
|
|
3732
|
-
const map = { undefined: '
|
|
3745
|
+
const map = { undefined: '0.5rem', none: '0', sm: '0.25rem', md: '0.75rem', lg: '1rem', xl: '1.5rem' }
|
|
3733
3746
|
return [['backdrop-filter', `blur(${map[match[1] ?? 'undefined']})`]]
|
|
3734
3747
|
}
|
|
3735
3748
|
match = base.match(/^ring(?:-(\d+))?$/)
|
|
3736
|
-
if (match) return [['box-shadow', `0 0 0 ${match[1] ?? 3}
|
|
3749
|
+
if (match) return [['box-shadow', `0 0 0 ${pixelNumberToRem(match[1] ?? 3)} var(--sf-ring-color, var(--color-ring))`]]
|
|
3737
3750
|
if (base === 'ring-offset-white') return [['--sf-ring-offset-color', 'var(--color-white)']]
|
|
3738
3751
|
if (base === 'ring-offset-background') return [['--sf-ring-offset-color', 'var(--color-background)']]
|
|
3739
3752
|
match = base.match(/^ring-offset-(\d+)$/)
|
|
3740
|
-
if (match) return [['--sf-ring-offset-width',
|
|
3753
|
+
if (match) return [['--sf-ring-offset-width', pixelNumberToRem(match[1])]]
|
|
3741
3754
|
if (base === 'outline') return [['outline-style', 'solid']]
|
|
3742
|
-
if (base === 'outline-none') return [['outline', '
|
|
3755
|
+
if (base === 'outline-none') return [['outline', '0.125rem solid transparent'], ['outline-offset', '0.125rem']]
|
|
3743
3756
|
match = base.match(/^outline-(\d+)$/)
|
|
3744
|
-
if (match) return [['outline-width',
|
|
3757
|
+
if (match) return [['outline-width', pixelNumberToRem(match[1])]]
|
|
3745
3758
|
match = base.match(/^outline-offset-(\d+)$/)
|
|
3746
|
-
if (match) return [['outline-offset',
|
|
3759
|
+
if (match) return [['outline-offset', pixelNumberToRem(match[1])]]
|
|
3747
3760
|
match = base.match(/^outline-(.+)$/)
|
|
3748
3761
|
if (match) {
|
|
3749
3762
|
const value = colourValue(match[1])
|
|
@@ -3944,6 +3957,7 @@ function buildTokensCss() {
|
|
|
3944
3957
|
|
|
3945
3958
|
${steps.join('\n')}
|
|
3946
3959
|
--type-caption-sm: var(--step--2);
|
|
3960
|
+
--text-base: var(--step-0);
|
|
3947
3961
|
|
|
3948
3962
|
${spaces.join('\n')}
|
|
3949
3963
|
${pairs.join('\n')}
|
|
@@ -4074,7 +4088,7 @@ function buildBaseCss() {
|
|
|
4074
4088
|
return `@layer reset {
|
|
4075
4089
|
*, *::before, *::after { box-sizing: border-box; }
|
|
4076
4090
|
*:where(:not(dialog)) { margin: 0; }
|
|
4077
|
-
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
|
|
4091
|
+
html { font-size: 100%; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
|
|
4078
4092
|
body { min-block-size: 100%; }
|
|
4079
4093
|
img, picture, video, canvas, svg { display: block; max-inline-size: 100%; }
|
|
4080
4094
|
img, video { block-size: auto; }
|
|
@@ -4084,11 +4098,15 @@ function buildBaseCss() {
|
|
|
4084
4098
|
}
|
|
4085
4099
|
|
|
4086
4100
|
@layer base {
|
|
4101
|
+
html {
|
|
4102
|
+
font-size: 100%;
|
|
4103
|
+
}
|
|
4104
|
+
|
|
4087
4105
|
body {
|
|
4088
4106
|
background: var(--color-background);
|
|
4089
4107
|
color: var(--color-foreground);
|
|
4090
4108
|
font-family: var(--font-inter, var(--sf-font-sans, ui-sans-serif, system-ui, sans-serif));
|
|
4091
|
-
font-size: var(--
|
|
4109
|
+
font-size: var(--text-base);
|
|
4092
4110
|
line-height: 1.5;
|
|
4093
4111
|
font-feature-settings: "rlig" 1, "calt" 1;
|
|
4094
4112
|
-webkit-font-smoothing: antialiased;
|
|
@@ -4116,7 +4134,7 @@ function buildBaseCss() {
|
|
|
4116
4134
|
:where(button:disabled, [aria-disabled="true"]) { cursor: not-allowed; }
|
|
4117
4135
|
|
|
4118
4136
|
:where(:focus-visible) {
|
|
4119
|
-
outline:
|
|
4137
|
+
outline: 0.125rem solid var(--color-ring);
|
|
4120
4138
|
outline-offset: .2rem;
|
|
4121
4139
|
}
|
|
4122
4140
|
|
package/components.css
CHANGED
|
@@ -432,6 +432,16 @@
|
|
|
432
432
|
to { transform: translateX(-50%); }
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
+
@media (prefers-reduced-motion: reduce) {
|
|
436
|
+
:where(.sf-button:hover, .sf-card--interactive:hover) {
|
|
437
|
+
transform: none;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.sf-marquee__track {
|
|
441
|
+
animation: none;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
435
445
|
.sf-command-list {
|
|
436
446
|
display: grid;
|
|
437
447
|
font-family: var(--sf-font-mono);
|
|
@@ -837,7 +847,7 @@
|
|
|
837
847
|
}
|
|
838
848
|
|
|
839
849
|
.sf-filter-bar {
|
|
840
|
-
align-items:
|
|
850
|
+
align-items: var(--sf-filter-bar-align, center);
|
|
841
851
|
background: var(--sf-colour-surface);
|
|
842
852
|
border: 1px solid var(--sf-colour-border);
|
|
843
853
|
border-radius: var(--sf-radius-panel);
|
|
@@ -984,6 +994,10 @@
|
|
|
984
994
|
inline-size: .42em;
|
|
985
995
|
}
|
|
986
996
|
|
|
997
|
+
:where(.sf-status[data-dot="false"], .sf-status--label)::before {
|
|
998
|
+
content: none;
|
|
999
|
+
}
|
|
1000
|
+
|
|
987
1001
|
.sf-status[data-tone="success"] {
|
|
988
1002
|
--sf-status-bg: var(--sf-colour-success-soft);
|
|
989
1003
|
--sf-status-border: color-mix(in oklch, var(--sf-colour-success) 34%, transparent);
|
|
@@ -1418,6 +1432,7 @@
|
|
|
1418
1432
|
:where(.sf-popover, .sf-menu-popover, .sf-tooltip, .sf-toast, .sf-banner, .sf-drawer):popover-open {
|
|
1419
1433
|
display: grid;
|
|
1420
1434
|
gap: var(--sf-space-xs);
|
|
1435
|
+
z-index: var(--sf-z-overlay);
|
|
1421
1436
|
}
|
|
1422
1437
|
|
|
1423
1438
|
.sf-popover {
|
|
@@ -1489,7 +1504,7 @@
|
|
|
1489
1504
|
inset-block-start: var(--sf-space-s);
|
|
1490
1505
|
inset-inline-end: var(--sf-space-s);
|
|
1491
1506
|
position: fixed;
|
|
1492
|
-
z-index:
|
|
1507
|
+
z-index: var(--sf-z-toast);
|
|
1493
1508
|
}
|
|
1494
1509
|
|
|
1495
1510
|
.sf-banner {
|
|
@@ -1604,6 +1619,7 @@
|
|
|
1604
1619
|
}
|
|
1605
1620
|
|
|
1606
1621
|
.sf-tab {
|
|
1622
|
+
align-items: center;
|
|
1607
1623
|
border-block-end: 0.125rem solid transparent;
|
|
1608
1624
|
border-radius: var(--sf-radius-full);
|
|
1609
1625
|
color: var(--sf-colour-muted);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Synced Flow Audit — June 2026 (v0.3.0)
|
|
2
|
+
|
|
3
|
+
Independent audit of `@syncedco/flow` at v0.3.0 (`a8a1f68`), run across three
|
|
4
|
+
passes: product coherence (claims vs shipped), CSS quality, and tooling/release
|
|
5
|
+
engineering. Findings are grounded in the source and in real consumer feedback
|
|
6
|
+
from the Synced CRM (`synced-crm`, the first production app consumer).
|
|
7
|
+
|
|
8
|
+
## Verdict
|
|
9
|
+
|
|
10
|
+
Flow delivers its **original model (websites/agency) almost completely** and
|
|
11
|
+
the **engineering discipline behind it impressively**. It falls short in three
|
|
12
|
+
places: **app-tier depth**, **advanced theming scope**, and a few product
|
|
13
|
+
positioning/docs gaps. It is a real, well-built system, and the positioning now
|
|
14
|
+
fits better as AI-friendly / agent-ready.
|
|
15
|
+
|
|
16
|
+
| Dimension | Rating | Summary |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| Fluid foundation (tokens, scales, layout) | ✅ Excellent | Genuine Utopia-style fluidity; modern CSS (layers, oklch, logical props, container queries); clean layer hygiene |
|
|
19
|
+
| Website/agency coverage | ✅ ~90% | Everything claimed ships; agency-ready without custom CSS |
|
|
20
|
+
| Tooling & release engineering | ✅ Excellent | 14 tested CLI commands, guardrails (size budgets, zero-dep, px policy), 40/40 tests, automated tag-driven publishing |
|
|
21
|
+
| App/SaaS tier (0.2–0.3) | ⚠️ ~60% | Real scaffolding (proven by the CRM) but thin; consumers carry 20–40% custom CSS |
|
|
22
|
+
| Dark mode | ✅ Patched after audit | `.sf-theme-dark` now overrides primary/status/soft/ring/glow tokens rather than inheriting light-tuned values |
|
|
23
|
+
| Theming config | ✅ Documented boundary | Config handles global light/dark token themes; advanced per-surface/multi-variant theming belongs in project CSS token scopes |
|
|
24
|
+
| AI positioning | ✅ Reframed | Positioning now uses AI-friendly / agent-ready language for the shipped CLI, skill, and agent guidance |
|
|
25
|
+
| Docs | ⚠️ Improving | Website docs thorough; CHANGELOG and theming-scope docs added; app-tier docs can still mature |
|
|
26
|
+
|
|
27
|
+
## Confirmed bugs (found by the CRM, verified in source)
|
|
28
|
+
|
|
29
|
+
1. **Drawer can't scroll** — fixed after audit by adding `block-size: 100dvh`
|
|
30
|
+
to `.sf-app-shell[data-mobile="drawer"] .sf-app-sidebar`, giving the
|
|
31
|
+
existing `overflow: auto` a real viewport-height scroll container.
|
|
32
|
+
2. **`.sf-tab` not vertically centred** — fixed after audit with
|
|
33
|
+
`align-items: center`.
|
|
34
|
+
3. **`.sf-filter-bar` `align-items: end`** — fixed after audit with
|
|
35
|
+
`align-items: var(--sf-filter-bar-align, center)`, preserving a project
|
|
36
|
+
escape hatch without adding a new component variant.
|
|
37
|
+
4. **`.sf-status` dot is mandatory** — fixed after audit with
|
|
38
|
+
`data-dot="false"` and `sf-status--label` opt-outs.
|
|
39
|
+
|
|
40
|
+
## Higher-severity findings the CRM did NOT hit
|
|
41
|
+
|
|
42
|
+
These mattered because no consumer had exercised them yet. They are now patched
|
|
43
|
+
or documented, but should still be validated in the next real consumer.
|
|
44
|
+
|
|
45
|
+
5. **Dark mode token coverage is critically incomplete** — fixed after audit
|
|
46
|
+
for the shipped base theme by adding dark-mode primary, state, soft, ring,
|
|
47
|
+
and glow token overrides.
|
|
48
|
+
6. **Z-index scale is compressed and undocumented** — fixed after audit with
|
|
49
|
+
public `--sf-z-*` tokens wired into header, backdrop, drawer, overlay, toast,
|
|
50
|
+
sticky, and skip-link layers.
|
|
51
|
+
7. **Missing `prefers-reduced-motion` guards** — fixed after audit for
|
|
52
|
+
`.sf-button:hover`, `.sf-card--interactive:hover`, and the marquee track.
|
|
53
|
+
|
|
54
|
+
## Theming model limitation (real, undocumented)
|
|
55
|
+
|
|
56
|
+
Config supports global light + `darkColours` + per-preset overrides. It does
|
|
57
|
+
not try to express per-surface / multi-variant theming (e.g. `[data-theme]` ×
|
|
58
|
+
`[data-surface]` matrices, per-tenant white-label, density-by-context). That is
|
|
59
|
+
a legitimate design choice for Synced Flow's scope, and `config-reference.md`
|
|
60
|
+
now documents the hand-written token-scope pattern for advanced cases.
|
|
61
|
+
|
|
62
|
+
## AI-Friendly Positioning
|
|
63
|
+
|
|
64
|
+
What ships is real and good: `catalog --json`, `suggest`, `pattern`, `recipe`,
|
|
65
|
+
`lint`, `doctor`, a packaged skill, and `agents install` for 8 targets. The
|
|
66
|
+
project now describes this as **AI-friendly** and agent-readable tooling rather
|
|
67
|
+
than implying runtime AI integration.
|
|
68
|
+
|
|
69
|
+
## App-tier coverage gaps (from real CRM adoption)
|
|
70
|
+
|
|
71
|
+
Shipped and adopted: app-shell, data-table, empty-state, filter-bar, status
|
|
72
|
+
chips, tabs, toast skin, form field shells. Still missing for production app
|
|
73
|
+
work (CRM kept custom or deferred): combobox/searchable select, pagination
|
|
74
|
+
depth (page size, jump-to-page), modal-with-form as a primitive (not just a
|
|
75
|
+
pattern), bulk-actions toolbar, multi-step/wizard, skeleton loading compounds,
|
|
76
|
+
sticky-header + horizontal-scroll tables. Kanban/timesheet/invoice-doc are
|
|
77
|
+
deliberately consumer-specific and out of scope.
|
|
78
|
+
|
|
79
|
+
## Strengths (keep doing)
|
|
80
|
+
|
|
81
|
+
- Real fluidity; modern CSS; excellent cascade-layer hygiene.
|
|
82
|
+
- Tooling/release engineering is the strongest area: guardrails enforce
|
|
83
|
+
zero-dependency, per-layer gzip size budgets, layer order, and a raw-px
|
|
84
|
+
policy; 40/40 tests pass; tag-driven OIDC publishing; complete exports map.
|
|
85
|
+
- CLI catalog currently in sync with shipped CSS (verified) — though by hand.
|
|
86
|
+
- AI agent guidance (skill + `agents install`) is current with 0.3.0 primitives.
|
|
87
|
+
|
|
88
|
+
## Recommended roadmap
|
|
89
|
+
|
|
90
|
+
**0.3.1 — bug patch (small, soon):**
|
|
91
|
+
1. Drawer viewport scroll container. ✅
|
|
92
|
+
2. `.sf-tab` `align-items: center`. ✅
|
|
93
|
+
3. `.sf-filter-bar` tokenized alignment with centered default. ✅
|
|
94
|
+
4. `.sf-status` dot opt-out. ✅
|
|
95
|
+
5. `prefers-reduced-motion` guards on hover transforms + marquee. ✅
|
|
96
|
+
|
|
97
|
+
**0.4.0 — structural gaps:**
|
|
98
|
+
6. Continue dark-mode validation with a real consumer using Flow's built-in
|
|
99
|
+
theme selectors.
|
|
100
|
+
7. Begin app-tier depth: combobox, pagination, modal-with-form, bulk-actions —
|
|
101
|
+
prioritised by what the CRM had to keep custom.
|
|
102
|
+
|
|
103
|
+
**Docs / positioning / CI (cheap, high-trust):**
|
|
104
|
+
8. CI: smoke-build the examples; add a test asserting the CLI catalog matches
|
|
105
|
+
the shipped CSS (in sync today only by hand); consider a CSS parse/lint and
|
|
106
|
+
a small visual-regression pass.
|
|
107
|
+
|
|
108
|
+
## Meta-observation
|
|
109
|
+
|
|
110
|
+
The four bugs the CRM surfaced are exactly the class only a real consumer finds
|
|
111
|
+
— the proving-ground loop is working as intended. The most useful next
|
|
112
|
+
validation step is getting a second consumer, or the CRM itself, onto Flow's
|
|
113
|
+
built-in dark mode and app primitives before 1.0.
|
package/docs/base-styling.md
CHANGED
|
@@ -111,3 +111,8 @@ The base layer uses cascade layers, logical properties, low-specificity
|
|
|
111
111
|
`prefers-reduced-motion`. Utopia informs the fluid type, space, and grid
|
|
112
112
|
tokens; Synced Flow owns the reset, accessibility helpers, and component
|
|
113
113
|
defaults.
|
|
114
|
+
|
|
115
|
+
The root element is kept at `font-size: 100%` rather than a fixed pixel value so
|
|
116
|
+
browser and user font-size settings remain respected. Body copy starts from the
|
|
117
|
+
fluid text token with `body { font-size: var(--sf-text-base); }`, where
|
|
118
|
+
`--sf-text-base` maps to the existing body type scale.
|
package/docs/config-reference.md
CHANGED
|
@@ -79,3 +79,30 @@ theme: {
|
|
|
79
79
|
},
|
|
80
80
|
}
|
|
81
81
|
```
|
|
82
|
+
|
|
83
|
+
## Theme Scope
|
|
84
|
+
|
|
85
|
+
The config theme model is intentionally global. Use `theme.colours` for the
|
|
86
|
+
default token set and `theme.darkColours` for `.sf-theme-dark` /
|
|
87
|
+
`[data-sf-theme="dark"]` overrides. This covers the common site or app case:
|
|
88
|
+
one brand token set, with an optional dark-mode token set.
|
|
89
|
+
|
|
90
|
+
Synced Flow does not try to encode every theme matrix in config. If a project
|
|
91
|
+
needs per-tenant branding, `[data-surface]` variants, density-by-context, or
|
|
92
|
+
other multi-axis theme rules, keep the reusable defaults in config and write
|
|
93
|
+
the extra token scopes in project CSS:
|
|
94
|
+
|
|
95
|
+
```css
|
|
96
|
+
[data-tenant="acme"] {
|
|
97
|
+
--sf-colour-primary: oklch(62% 0.19 252);
|
|
98
|
+
--sf-colour-primary-hover: oklch(56% 0.19 252);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
[data-surface="inset"] {
|
|
102
|
+
--sf-colour-surface: var(--sf-colour-surface-inset);
|
|
103
|
+
--sf-card-padding: var(--sf-space-s);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Use this pattern for deliberate product theming. Avoid scattering one-off
|
|
108
|
+
colour values through page components when a value can live in a token scope.
|
|
@@ -10,13 +10,14 @@ Use the `--sf-*` variables as the stable CSS foundation.
|
|
|
10
10
|
| Layer | Examples | Use for |
|
|
11
11
|
| --- | --- | --- |
|
|
12
12
|
| Font | `--sf-font-sans`, `--sf-font-display`, `--sf-font-mono` | Site typography families. |
|
|
13
|
-
| Type | `--sf-type-body`, `--sf-type-lead`, `--sf-type-h1`, `--sf-type-display` | Fluid text sizes. |
|
|
13
|
+
| Type | `--sf-text-base`, `--sf-type-body`, `--sf-type-lead`, `--sf-type-h1`, `--sf-type-display` | Fluid text sizes. |
|
|
14
14
|
| Space | `--sf-space-s`, `--sf-space-m-l`, `--sf-space-xl-2xl` | Fluid padding, margin, and gaps. |
|
|
15
15
|
| Radius | `--sf-radius-control`, `--sf-radius-panel`, `--sf-radius-full` | Controls, panels, pills. |
|
|
16
16
|
| Colour | `--sf-colour-background`, `--sf-colour-surface`, `--sf-colour-primary` | Semantic UI colour roles. |
|
|
17
17
|
| App colour | `--sf-colour-surface-hover`, `--sf-colour-surface-raised-2`, `--sf-colour-border-subtle`, `--sf-colour-backdrop` | App surfaces, hover states, subtle dividers, and overlays. |
|
|
18
18
|
| Categorical colour | `--sf-colour-categorical-1` ... `--sf-colour-categorical-6` | Charts, stages, labels, and recurring data accents. |
|
|
19
19
|
| State | `--sf-colour-success`, `--sf-colour-warning`, `--sf-colour-danger`, `--sf-colour-info` | Feedback and notices. |
|
|
20
|
+
| Z-index | `--sf-z-header`, `--sf-z-backdrop`, `--sf-z-drawer`, `--sf-z-overlay`, `--sf-z-toast` | Shared stacking slots for app chrome, drawers, popovers, and feedback. |
|
|
20
21
|
| Motion | `--sf-duration-fast`, `--sf-duration-normal`, `--sf-ease-standard` | Consistent transitions. |
|
|
21
22
|
| Component | `--sf-button-*`, `--sf-card-*`, `--sf-input-*`, `--sf-alert-*` | Reusable component defaults. |
|
|
22
23
|
| Accessibility | `:focus-visible`, `:target`, `[aria-invalid]`, `[aria-current]`, `[aria-expanded]`, `[aria-selected]`, `[aria-busy]`, `[aria-disabled]` | Visible native and ARIA states. |
|
package/docs/tokens.md
CHANGED
|
@@ -64,10 +64,15 @@ Type and space tokens use Utopia-style `clamp()` values.
|
|
|
64
64
|
|
|
65
65
|
```css
|
|
66
66
|
--sf-step-0: clamp(1rem, 0.9617rem + 0.1701vw, 1.125rem);
|
|
67
|
+
--sf-type-body: var(--sf-step-0);
|
|
68
|
+
--sf-text-base: var(--sf-type-body);
|
|
67
69
|
--sf-space-s-l: clamp(1rem, 0.6173rem + 1.7007vw, 2.25rem);
|
|
68
70
|
```
|
|
69
71
|
|
|
70
|
-
Prefer these tokens for new project CSS instead of fixed pixel values.
|
|
72
|
+
Prefer these tokens for new project CSS instead of fixed pixel values. The root
|
|
73
|
+
`html` element stays at `font-size: 100%` so browser and user accessibility
|
|
74
|
+
settings remain in control; Synced Flow starts its fluid body scale from
|
|
75
|
+
`--sf-text-base`.
|
|
71
76
|
|
|
72
77
|
## Core Starter Tokens
|
|
73
78
|
|
|
@@ -86,6 +91,8 @@ The generated core includes enough semantic variables for common website UI:
|
|
|
86
91
|
- structure: `--sf-colour-border-subtle`, `--sf-colour-border`,
|
|
87
92
|
`--sf-colour-border-strong`, `--sf-colour-backdrop`,
|
|
88
93
|
`--sf-radius-control`, `--sf-radius-panel`, `--sf-shadow-*`
|
|
94
|
+
- stacking: `--sf-z-sticky`, `--sf-z-header`, `--sf-z-backdrop`,
|
|
95
|
+
`--sf-z-drawer`, `--sf-z-overlay`, `--sf-z-toast`, `--sf-z-skip-link`
|
|
89
96
|
- data accents: `--sf-colour-categorical-1` through
|
|
90
97
|
`--sf-colour-categorical-6`
|
|
91
98
|
|
package/docs/why-synced-flow.md
CHANGED
|
@@ -22,7 +22,7 @@ Synced Flow is designed to reduce the number of styling decisions made from scra
|
|
|
22
22
|
|
|
23
23
|
Synced Flow is not trying to be a Tailwind clone, a Bootstrap replacement, or a React component library.
|
|
24
24
|
|
|
25
|
-
It is an AI-
|
|
25
|
+
It is an AI-friendly fluid CSS design system for brand-consistent websites and agency builds.
|
|
26
26
|
|
|
27
27
|
Use it when you want:
|
|
28
28
|
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
--step-6: clamp(2.986rem, 2.5863rem + 1.7763vw, 4.2915rem);
|
|
20
20
|
--step-7: clamp(3.5832rem, 3.0379rem + 2.4235vw, 5.3644rem);
|
|
21
21
|
--step-8: clamp(4.2998rem, 3.5634rem + 3.2731vw, 6.7055rem);
|
|
22
|
+
--type-caption-sm: var(--step--2);
|
|
23
|
+
--text-base: var(--step-0);
|
|
22
24
|
|
|
23
25
|
--space-3xs: clamp(0.25rem, 0.2404rem + 0.0425vw, 0.2813rem);
|
|
24
26
|
--space-2xs: clamp(0.5rem, 0.4809rem + 0.085vw, 0.5625rem);
|
|
@@ -88,7 +90,9 @@
|
|
|
88
90
|
--color-destructive: var(--sf-colour-destructive, oklch(55% 0.2 28));
|
|
89
91
|
--color-destructive-foreground: var(--sf-colour-destructive-foreground, oklch(98% 0.01 95));
|
|
90
92
|
--color-border: var(--sf-colour-border, oklch(100% 0 0 / 0.12));
|
|
93
|
+
--color-border-subtle: var(--sf-colour-border-subtle, oklch(100% 0 0 / 0.08));
|
|
91
94
|
--color-input: var(--sf-colour-input, oklch(100% 0 0 / 0.12));
|
|
95
|
+
--color-backdrop: var(--sf-colour-backdrop, oklch(0% 0 0 / 0.56));
|
|
92
96
|
--color-ring: var(--sf-colour-ring, oklch(69% 0.18 44));
|
|
93
97
|
--color-heading: var(--sf-colour-heading, oklch(20% 0.02 18));
|
|
94
98
|
--color-page-text: var(--sf-colour-page-text, oklch(23% 0.02 18));
|
|
@@ -97,6 +101,14 @@
|
|
|
97
101
|
--color-subdued: var(--sf-colour-subdued, oklch(42% 0.02 20));
|
|
98
102
|
--color-surface: var(--sf-colour-surface, oklch(96% 0.015 80));
|
|
99
103
|
--color-surface-alt: var(--sf-colour-surface-alt, oklch(96.6% 0.018 82));
|
|
104
|
+
--color-surface-hover: var(--sf-colour-surface-hover, color-mix(in oklch, var(--color-surface-alt) 86%, var(--color-primary)));
|
|
105
|
+
--color-surface-raised-2: var(--sf-colour-surface-raised-2, color-mix(in oklch, var(--color-surface) 86%, white));
|
|
106
|
+
--color-categorical-1: var(--sf-colour-categorical-1, var(--color-primary));
|
|
107
|
+
--color-categorical-2: var(--sf-colour-categorical-2, var(--color-green));
|
|
108
|
+
--color-categorical-3: var(--sf-colour-categorical-3, var(--color-deco-blue));
|
|
109
|
+
--color-categorical-4: var(--sf-colour-categorical-4, var(--color-deco-lime));
|
|
110
|
+
--color-categorical-5: var(--sf-colour-categorical-5, var(--color-warm-icon));
|
|
111
|
+
--color-categorical-6: var(--sf-colour-categorical-6, var(--color-purple-icon));
|
|
100
112
|
--color-hero-bg: oklch(8% 0.02 260);
|
|
101
113
|
--color-green: var(--sf-colour-green, oklch(55% 0.12 148));
|
|
102
114
|
--color-green-dark: var(--sf-colour-green-dark, oklch(45% 0.12 148));
|
|
@@ -168,6 +180,8 @@
|
|
|
168
180
|
--color-surface: var(--sf-colour-surface);
|
|
169
181
|
--sf-colour-surface-alt: oklch(94% 0.02 80);
|
|
170
182
|
--color-surface-alt: var(--sf-colour-surface-alt);
|
|
183
|
+
--sf-colour-surface-elevated: var(--sf-colour-surface-raised);
|
|
184
|
+
--color-surface-elevated: var(--sf-colour-surface-elevated);
|
|
171
185
|
--sf-colour-border: oklch(18% 0.022 55 / 0.14);
|
|
172
186
|
--color-border: var(--sf-colour-border);
|
|
173
187
|
--sf-colour-primary: oklch(58% 0.17 42);
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
--step-6: clamp(2.986rem, 2.5863rem + 1.7763vw, 4.2915rem);
|
|
20
20
|
--step-7: clamp(3.5832rem, 3.0379rem + 2.4235vw, 5.3644rem);
|
|
21
21
|
--step-8: clamp(4.2998rem, 3.5634rem + 3.2731vw, 6.7055rem);
|
|
22
|
+
--type-caption-sm: var(--step--2);
|
|
23
|
+
--text-base: var(--step-0);
|
|
22
24
|
|
|
23
25
|
--space-3xs: clamp(0.25rem, 0.2404rem + 0.0425vw, 0.2813rem);
|
|
24
26
|
--space-2xs: clamp(0.5rem, 0.4809rem + 0.085vw, 0.5625rem);
|
|
@@ -88,7 +90,9 @@
|
|
|
88
90
|
--color-destructive: var(--sf-colour-destructive, oklch(55% 0.2 28));
|
|
89
91
|
--color-destructive-foreground: var(--sf-colour-destructive-foreground, oklch(98% 0.01 95));
|
|
90
92
|
--color-border: var(--sf-colour-border, oklch(100% 0 0 / 0.12));
|
|
93
|
+
--color-border-subtle: var(--sf-colour-border-subtle, oklch(100% 0 0 / 0.08));
|
|
91
94
|
--color-input: var(--sf-colour-input, oklch(100% 0 0 / 0.12));
|
|
95
|
+
--color-backdrop: var(--sf-colour-backdrop, oklch(0% 0 0 / 0.56));
|
|
92
96
|
--color-ring: var(--sf-colour-ring, oklch(69% 0.18 44));
|
|
93
97
|
--color-heading: var(--sf-colour-heading, oklch(20% 0.02 18));
|
|
94
98
|
--color-page-text: var(--sf-colour-page-text, oklch(23% 0.02 18));
|
|
@@ -97,6 +101,14 @@
|
|
|
97
101
|
--color-subdued: var(--sf-colour-subdued, oklch(42% 0.02 20));
|
|
98
102
|
--color-surface: var(--sf-colour-surface, oklch(96% 0.015 80));
|
|
99
103
|
--color-surface-alt: var(--sf-colour-surface-alt, oklch(96.6% 0.018 82));
|
|
104
|
+
--color-surface-hover: var(--sf-colour-surface-hover, color-mix(in oklch, var(--color-surface-alt) 86%, var(--color-primary)));
|
|
105
|
+
--color-surface-raised-2: var(--sf-colour-surface-raised-2, color-mix(in oklch, var(--color-surface) 86%, white));
|
|
106
|
+
--color-categorical-1: var(--sf-colour-categorical-1, var(--color-primary));
|
|
107
|
+
--color-categorical-2: var(--sf-colour-categorical-2, var(--color-green));
|
|
108
|
+
--color-categorical-3: var(--sf-colour-categorical-3, var(--color-deco-blue));
|
|
109
|
+
--color-categorical-4: var(--sf-colour-categorical-4, var(--color-deco-lime));
|
|
110
|
+
--color-categorical-5: var(--sf-colour-categorical-5, var(--color-warm-icon));
|
|
111
|
+
--color-categorical-6: var(--sf-colour-categorical-6, var(--color-purple-icon));
|
|
100
112
|
--color-hero-bg: oklch(8% 0.02 260);
|
|
101
113
|
--color-green: var(--sf-colour-green, oklch(55% 0.12 148));
|
|
102
114
|
--color-green-dark: var(--sf-colour-green-dark, oklch(45% 0.12 148));
|
|
@@ -168,6 +180,8 @@
|
|
|
168
180
|
--color-surface: var(--sf-colour-surface);
|
|
169
181
|
--sf-colour-surface-alt: oklch(96.4% 0.011 80);
|
|
170
182
|
--color-surface-alt: var(--sf-colour-surface-alt);
|
|
183
|
+
--sf-colour-surface-elevated: var(--sf-colour-surface-raised);
|
|
184
|
+
--color-surface-elevated: var(--sf-colour-surface-elevated);
|
|
171
185
|
--sf-colour-border: oklch(18% 0.026 250 / 0.12);
|
|
172
186
|
--color-border: var(--sf-colour-border);
|
|
173
187
|
--sf-colour-primary: oklch(68% 0.18 44);
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
--step-6: clamp(2.986rem, 2.5863rem + 1.7763vw, 4.2915rem);
|
|
20
20
|
--step-7: clamp(3.5832rem, 3.0379rem + 2.4235vw, 5.3644rem);
|
|
21
21
|
--step-8: clamp(4.2998rem, 3.5634rem + 3.2731vw, 6.7055rem);
|
|
22
|
+
--type-caption-sm: var(--step--2);
|
|
23
|
+
--text-base: var(--step-0);
|
|
22
24
|
|
|
23
25
|
--space-3xs: clamp(0.25rem, 0.2404rem + 0.0425vw, 0.2813rem);
|
|
24
26
|
--space-2xs: clamp(0.5rem, 0.4809rem + 0.085vw, 0.5625rem);
|
|
@@ -88,7 +90,9 @@
|
|
|
88
90
|
--color-destructive: var(--sf-colour-destructive, oklch(55% 0.2 28));
|
|
89
91
|
--color-destructive-foreground: var(--sf-colour-destructive-foreground, oklch(98% 0.01 95));
|
|
90
92
|
--color-border: var(--sf-colour-border, oklch(100% 0 0 / 0.12));
|
|
93
|
+
--color-border-subtle: var(--sf-colour-border-subtle, oklch(100% 0 0 / 0.08));
|
|
91
94
|
--color-input: var(--sf-colour-input, oklch(100% 0 0 / 0.12));
|
|
95
|
+
--color-backdrop: var(--sf-colour-backdrop, oklch(0% 0 0 / 0.56));
|
|
92
96
|
--color-ring: var(--sf-colour-ring, oklch(69% 0.18 44));
|
|
93
97
|
--color-heading: var(--sf-colour-heading, oklch(20% 0.02 18));
|
|
94
98
|
--color-page-text: var(--sf-colour-page-text, oklch(23% 0.02 18));
|
|
@@ -97,6 +101,14 @@
|
|
|
97
101
|
--color-subdued: var(--sf-colour-subdued, oklch(42% 0.02 20));
|
|
98
102
|
--color-surface: var(--sf-colour-surface, oklch(96% 0.015 80));
|
|
99
103
|
--color-surface-alt: var(--sf-colour-surface-alt, oklch(96.6% 0.018 82));
|
|
104
|
+
--color-surface-hover: var(--sf-colour-surface-hover, color-mix(in oklch, var(--color-surface-alt) 86%, var(--color-primary)));
|
|
105
|
+
--color-surface-raised-2: var(--sf-colour-surface-raised-2, color-mix(in oklch, var(--color-surface) 86%, white));
|
|
106
|
+
--color-categorical-1: var(--sf-colour-categorical-1, var(--color-primary));
|
|
107
|
+
--color-categorical-2: var(--sf-colour-categorical-2, var(--color-green));
|
|
108
|
+
--color-categorical-3: var(--sf-colour-categorical-3, var(--color-deco-blue));
|
|
109
|
+
--color-categorical-4: var(--sf-colour-categorical-4, var(--color-deco-lime));
|
|
110
|
+
--color-categorical-5: var(--sf-colour-categorical-5, var(--color-warm-icon));
|
|
111
|
+
--color-categorical-6: var(--sf-colour-categorical-6, var(--color-purple-icon));
|
|
100
112
|
--color-hero-bg: oklch(8% 0.02 260);
|
|
101
113
|
--color-green: var(--sf-colour-green, oklch(55% 0.12 148));
|
|
102
114
|
--color-green-dark: var(--sf-colour-green-dark, oklch(45% 0.12 148));
|
|
@@ -168,6 +180,8 @@
|
|
|
168
180
|
--color-surface: var(--sf-colour-surface);
|
|
169
181
|
--sf-colour-surface-alt: oklch(96.4% 0.011 80);
|
|
170
182
|
--color-surface-alt: var(--sf-colour-surface-alt);
|
|
183
|
+
--sf-colour-surface-elevated: var(--sf-colour-surface-raised);
|
|
184
|
+
--color-surface-elevated: var(--sf-colour-surface-elevated);
|
|
171
185
|
--sf-colour-border: oklch(18% 0.026 250 / 0.12);
|
|
172
186
|
--color-border: var(--sf-colour-border);
|
|
173
187
|
--sf-colour-primary: oklch(68% 0.18 44);
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
--step-6: clamp(2.986rem, 2.5863rem + 1.7763vw, 4.2915rem);
|
|
20
20
|
--step-7: clamp(3.5832rem, 3.0379rem + 2.4235vw, 5.3644rem);
|
|
21
21
|
--step-8: clamp(4.2998rem, 3.5634rem + 3.2731vw, 6.7055rem);
|
|
22
|
+
--type-caption-sm: var(--step--2);
|
|
23
|
+
--text-base: var(--step-0);
|
|
22
24
|
|
|
23
25
|
--space-3xs: clamp(0.25rem, 0.2404rem + 0.0425vw, 0.2813rem);
|
|
24
26
|
--space-2xs: clamp(0.5rem, 0.4809rem + 0.085vw, 0.5625rem);
|
|
@@ -88,7 +90,9 @@
|
|
|
88
90
|
--color-destructive: var(--sf-colour-destructive, oklch(55% 0.2 28));
|
|
89
91
|
--color-destructive-foreground: var(--sf-colour-destructive-foreground, oklch(98% 0.01 95));
|
|
90
92
|
--color-border: var(--sf-colour-border, oklch(100% 0 0 / 0.12));
|
|
93
|
+
--color-border-subtle: var(--sf-colour-border-subtle, oklch(100% 0 0 / 0.08));
|
|
91
94
|
--color-input: var(--sf-colour-input, oklch(100% 0 0 / 0.12));
|
|
95
|
+
--color-backdrop: var(--sf-colour-backdrop, oklch(0% 0 0 / 0.56));
|
|
92
96
|
--color-ring: var(--sf-colour-ring, oklch(69% 0.18 44));
|
|
93
97
|
--color-heading: var(--sf-colour-heading, oklch(20% 0.02 18));
|
|
94
98
|
--color-page-text: var(--sf-colour-page-text, oklch(23% 0.02 18));
|
|
@@ -97,6 +101,14 @@
|
|
|
97
101
|
--color-subdued: var(--sf-colour-subdued, oklch(42% 0.02 20));
|
|
98
102
|
--color-surface: var(--sf-colour-surface, oklch(96% 0.015 80));
|
|
99
103
|
--color-surface-alt: var(--sf-colour-surface-alt, oklch(96.6% 0.018 82));
|
|
104
|
+
--color-surface-hover: var(--sf-colour-surface-hover, color-mix(in oklch, var(--color-surface-alt) 86%, var(--color-primary)));
|
|
105
|
+
--color-surface-raised-2: var(--sf-colour-surface-raised-2, color-mix(in oklch, var(--color-surface) 86%, white));
|
|
106
|
+
--color-categorical-1: var(--sf-colour-categorical-1, var(--color-primary));
|
|
107
|
+
--color-categorical-2: var(--sf-colour-categorical-2, var(--color-green));
|
|
108
|
+
--color-categorical-3: var(--sf-colour-categorical-3, var(--color-deco-blue));
|
|
109
|
+
--color-categorical-4: var(--sf-colour-categorical-4, var(--color-deco-lime));
|
|
110
|
+
--color-categorical-5: var(--sf-colour-categorical-5, var(--color-warm-icon));
|
|
111
|
+
--color-categorical-6: var(--sf-colour-categorical-6, var(--color-purple-icon));
|
|
100
112
|
--color-hero-bg: oklch(8% 0.02 260);
|
|
101
113
|
--color-green: var(--sf-colour-green, oklch(55% 0.12 148));
|
|
102
114
|
--color-green-dark: var(--sf-colour-green-dark, oklch(45% 0.12 148));
|
|
@@ -168,6 +180,8 @@
|
|
|
168
180
|
--color-surface: var(--sf-colour-surface);
|
|
169
181
|
--sf-colour-surface-alt: oklch(96.4% 0.011 80);
|
|
170
182
|
--color-surface-alt: var(--sf-colour-surface-alt);
|
|
183
|
+
--sf-colour-surface-elevated: var(--sf-colour-surface-raised);
|
|
184
|
+
--color-surface-elevated: var(--sf-colour-surface-elevated);
|
|
171
185
|
--sf-colour-border: oklch(18% 0.026 250 / 0.12);
|
|
172
186
|
--color-border: var(--sf-colour-border);
|
|
173
187
|
--sf-colour-primary: oklch(68% 0.18 44);
|