@syncedco/flow 0.2.0 → 0.3.0

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/components.css CHANGED
@@ -885,6 +885,76 @@
885
885
  --sf-data-table-padding-inline: var(--sf-space-xs);
886
886
  }
887
887
 
888
+ .sf-data-table :where(th button, .sf-table-sort) {
889
+ align-items: center;
890
+ color: inherit;
891
+ display: inline-flex;
892
+ font: inherit;
893
+ font-weight: inherit;
894
+ gap: var(--sf-space-3xs);
895
+ padding: 0;
896
+ }
897
+
898
+ .sf-data-table :where(th button, .sf-table-sort)[aria-sort]::after {
899
+ color: var(--sf-colour-subtle);
900
+ content: "\2195";
901
+ font-size: var(--sf-step--2);
902
+ }
903
+
904
+ .sf-data-table :where(th button, .sf-table-sort)[aria-sort="ascending"]::after { content: "\2191"; }
905
+ .sf-data-table :where(th button, .sf-table-sort)[aria-sort="descending"]::after { content: "\2193"; }
906
+
907
+ .sf-data-table tr[data-clickable="true"] {
908
+ cursor: pointer;
909
+ }
910
+
911
+ .sf-data-table tr[data-clickable="true"]:focus-within {
912
+ outline: 0.125rem solid var(--sf-colour-ring);
913
+ outline-offset: -0.125rem;
914
+ }
915
+
916
+ @container (max-width: 38rem) {
917
+ .sf-table-wrap[data-view="cards"] {
918
+ border: 0;
919
+ overflow: visible;
920
+ }
921
+
922
+ .sf-table-wrap[data-view="cards"] .sf-data-table,
923
+ .sf-table-wrap[data-view="cards"] .sf-data-table :where(thead, tbody, tr, th, td) {
924
+ display: block;
925
+ min-inline-size: 0;
926
+ }
927
+
928
+ .sf-table-wrap[data-view="cards"] .sf-data-table thead {
929
+ display: none;
930
+ }
931
+
932
+ .sf-table-wrap[data-view="cards"] .sf-data-table tr {
933
+ background: var(--sf-colour-surface);
934
+ border: 1px solid var(--sf-colour-border);
935
+ border-radius: var(--sf-radius-panel);
936
+ margin-block-end: var(--sf-space-xs);
937
+ padding: var(--sf-space-xs);
938
+ }
939
+
940
+ .sf-table-wrap[data-view="cards"] .sf-data-table td {
941
+ align-items: center;
942
+ border: 0;
943
+ display: flex;
944
+ gap: var(--sf-space-s);
945
+ justify-content: space-between;
946
+ padding-block: var(--sf-space-2xs);
947
+ padding-inline: 0;
948
+ }
949
+
950
+ .sf-table-wrap[data-view="cards"] .sf-data-table td::before {
951
+ color: var(--sf-colour-muted);
952
+ content: attr(data-label);
953
+ font-size: var(--sf-step--1);
954
+ font-weight: 800;
955
+ }
956
+ }
957
+
888
958
  .sf-status {
889
959
  align-items: center;
890
960
  background: var(--sf-status-bg, var(--sf-colour-surface-alt));
@@ -900,6 +970,12 @@
900
970
  padding-inline: .7em;
901
971
  }
902
972
 
973
+ .sf-status[data-size="sm"] {
974
+ font-size: var(--sf-step--2);
975
+ min-block-size: 1.35rem;
976
+ padding-inline: .55em;
977
+ }
978
+
903
979
  .sf-status::before {
904
980
  background: currentColor;
905
981
  block-size: .42em;
@@ -1055,6 +1131,13 @@
1055
1131
  gap: var(--sf-form-gap, var(--sf-space-s-m));
1056
1132
  }
1057
1133
 
1134
+ .sf-form[data-density="compact"],
1135
+ .sf-field[data-density="compact"] {
1136
+ --sf-form-gap: var(--sf-space-xs);
1137
+ --sf-input-block-size: 2.25rem;
1138
+ --sf-input-padding-inline: var(--sf-space-xs);
1139
+ }
1140
+
1058
1141
  .sf-fieldset {
1059
1142
  border: 1px solid var(--sf-colour-border);
1060
1143
  border-radius: var(--sf-radius-panel);
@@ -1120,6 +1203,45 @@
1120
1203
  transition: background-color var(--sf-duration-fast) var(--sf-ease-standard), border-color var(--sf-duration-fast) var(--sf-ease-standard), box-shadow var(--sf-duration-fast) var(--sf-ease-standard);
1121
1204
  }
1122
1205
 
1206
+ .sf-search {
1207
+ position: relative;
1208
+ }
1209
+
1210
+ .sf-search::before {
1211
+ block-size: .75em;
1212
+ border: 0.125em solid currentColor;
1213
+ border-radius: var(--sf-radius-full);
1214
+ color: var(--sf-colour-subtle);
1215
+ content: "";
1216
+ inline-size: .75em;
1217
+ inset-block-start: 50%;
1218
+ inset-inline-start: var(--sf-space-xs);
1219
+ line-height: 1;
1220
+ position: absolute;
1221
+ transform: translateY(-58%);
1222
+ }
1223
+
1224
+ .sf-search::after {
1225
+ background: var(--sf-colour-subtle);
1226
+ block-size: .125em;
1227
+ content: "";
1228
+ inline-size: .45em;
1229
+ inset-block-start: 50%;
1230
+ inset-inline-start: calc(var(--sf-space-xs) + .58em);
1231
+ position: absolute;
1232
+ transform: translateY(120%) rotate(45deg);
1233
+ transform-origin: inline-start center;
1234
+ }
1235
+
1236
+ .sf-search > .sf-input {
1237
+ padding-inline-start: var(--sf-space-l);
1238
+ }
1239
+
1240
+ .sf-help[data-density="compact"],
1241
+ .sf-error[data-density="compact"] {
1242
+ font-size: var(--sf-step--2);
1243
+ }
1244
+
1123
1245
  .sf-textarea {
1124
1246
  min-block-size: var(--sf-textarea-block-size, 8rem);
1125
1247
  padding-block: var(--sf-space-xs);
@@ -1240,7 +1362,7 @@
1240
1362
  .sf-dialog::backdrop,
1241
1363
  .sf-popover::backdrop,
1242
1364
  .sf-drawer::backdrop {
1243
- background: oklch(0% 0 0 / 0.48);
1365
+ background: var(--sf-colour-backdrop);
1244
1366
  }
1245
1367
 
1246
1368
  .sf-dialog__header,
@@ -1266,6 +1388,11 @@
1266
1388
  justify-content: flex-end;
1267
1389
  }
1268
1390
 
1391
+ .sf-dialog[data-layout="form"] .sf-dialog__body {
1392
+ display: grid;
1393
+ gap: var(--sf-space-s);
1394
+ }
1395
+
1269
1396
  .sf-popover,
1270
1397
  .sf-menu-popover,
1271
1398
  .sf-tooltip,
@@ -1319,6 +1446,16 @@
1319
1446
  background: var(--sf-colour-surface-alt);
1320
1447
  }
1321
1448
 
1449
+ .sf-menu :where(a, button)[data-variant="danger"],
1450
+ .sf-menu-popover :where(a, button)[data-variant="danger"] {
1451
+ color: var(--sf-colour-danger);
1452
+ }
1453
+
1454
+ .sf-menu :where(a, button)[data-variant="danger"]:hover,
1455
+ .sf-menu-popover :where(a, button)[data-variant="danger"]:hover {
1456
+ background: var(--sf-colour-danger-soft);
1457
+ }
1458
+
1322
1459
  .sf-tooltip {
1323
1460
  border-radius: var(--sf-radius-md);
1324
1461
  box-shadow: var(--sf-shadow-md);
@@ -1345,6 +1482,16 @@
1345
1482
  top: var(--sf-space-s);
1346
1483
  }
1347
1484
 
1485
+ .sf-toast-stack {
1486
+ display: grid;
1487
+ gap: var(--sf-space-xs);
1488
+ inline-size: min(100% - (var(--sf-gutter) * 2), var(--sf-toast-width, 24rem));
1489
+ inset-block-start: var(--sf-space-s);
1490
+ inset-inline-end: var(--sf-space-s);
1491
+ position: fixed;
1492
+ z-index: 50;
1493
+ }
1494
+
1348
1495
  .sf-banner {
1349
1496
  block-size: auto;
1350
1497
  border-radius: var(--sf-radius-lg);
@@ -1458,14 +1605,21 @@
1458
1605
 
1459
1606
  .sf-tab {
1460
1607
  border-block-end: 0.125rem solid transparent;
1608
+ border-radius: var(--sf-radius-full);
1461
1609
  color: var(--sf-colour-muted);
1462
1610
  display: inline-flex;
1611
+ gap: var(--sf-space-2xs);
1463
1612
  font-weight: 800;
1464
1613
  padding-block: var(--sf-space-xs);
1465
1614
  padding-inline: var(--sf-space-s);
1466
1615
  text-decoration: none;
1467
1616
  }
1468
1617
 
1618
+ .sf-tab[data-variant="pill"] {
1619
+ border: 1px solid transparent;
1620
+ padding-block: var(--sf-space-2xs);
1621
+ }
1622
+
1469
1623
  .sf-tab:hover,
1470
1624
  .sf-tab[aria-selected="true"],
1471
1625
  .sf-tab[data-state="active"] {
@@ -1473,6 +1627,24 @@
1473
1627
  color: var(--sf-colour-foreground);
1474
1628
  }
1475
1629
 
1630
+ .sf-tab[data-variant="pill"]:hover,
1631
+ .sf-tab[data-variant="pill"][aria-selected="true"],
1632
+ .sf-tab[data-variant="pill"][data-state="active"] {
1633
+ background: var(--sf-colour-primary-soft);
1634
+ border-color: var(--sf-colour-primary-soft-border);
1635
+ color: var(--sf-colour-primary);
1636
+ }
1637
+
1638
+ .sf-tab__count {
1639
+ background: var(--sf-colour-surface-alt);
1640
+ border: 1px solid var(--sf-colour-border);
1641
+ border-radius: var(--sf-radius-full);
1642
+ color: var(--sf-colour-muted);
1643
+ font-size: var(--sf-step--2);
1644
+ line-height: 1;
1645
+ padding: .25em .55em;
1646
+ }
1647
+
1476
1648
  .sf-tab-panel {
1477
1649
  background: var(--sf-colour-surface);
1478
1650
  border: 1px solid var(--sf-colour-border);
@@ -13,9 +13,9 @@ These are intended for application code and examples.
13
13
  | Imports | `@syncedco/flow/styles.css`, `tokens.css`, `reset.css`, `base.css`, `defaults.css`, `layout.css`, `components.css`, `utilities.css` |
14
14
  | Tokens | Unlayered `--sf-*` custom properties emitted by `tokens.css` |
15
15
  | Theme config | `theme.fonts`, `theme.colours`, `theme.darkColours`, `theme.radii`, `theme.layout`, `theme.components` |
16
- | Layout classes | `sf-container`, `sf-section`, `sf-stack`, `sf-flow`, `sf-cluster`, `sf-repel`, `sf-toolbar`, `sf-app-shell`, `sf-app-sidebar`, `sf-app-main`, `sf-auto-grid`, `sf-switcher`, `sf-sidebar`, `sf-split`, `sf-frame`, `sf-cover`, `sf-metric-grid`, `sf-pipeline` |
17
- | Components | `sf-button`, `sf-icon`, `sf-icon-button`, `sf-avatar`, `sf-chart`, `sf-meter`, `sf-progress`, `sf-skeleton`, `sf-filter-bar`, `sf-data-table`, `sf-status`, `sf-data-list`, `sf-empty-state`, `sf-settings-section`, `sf-detail-panel`, `sf-card`, `sf-surface`, `sf-hero`, `sf-nav`, `sf-form`, `sf-field`, `sf-input`, `sf-select`, `sf-textarea`, `sf-check`, `sf-alert`, `sf-badge`, `sf-section-header`, `sf-kicker` |
18
- | Native components | `sf-dialog`, `sf-popover`, `sf-tooltip`, `sf-drawer`, `sf-drawer--stack`, `sf-disclosure`, `sf-accordion`, `sf-tabs`, `sf-menu`, `sf-breadcrumb`, `sf-pagination` |
16
+ | Layout classes | `sf-container`, `sf-section`, `sf-stack`, `sf-flow`, `sf-cluster`, `sf-repel`, `sf-toolbar`, `sf-app-shell`, `sf-app-header`, `sf-app-sidebar`, `sf-app-backdrop`, `sf-app-main`, `sf-auto-grid`, `sf-switcher`, `sf-sidebar`, `sf-split`, `sf-frame`, `sf-cover`, `sf-metric-grid`, `sf-pipeline` |
17
+ | Components | `sf-button`, `sf-icon`, `sf-icon-button`, `sf-avatar`, `sf-chart`, `sf-meter`, `sf-progress`, `sf-skeleton`, `sf-filter-bar`, `sf-search`, `sf-data-table`, `sf-table-sort`, `sf-status`, `sf-data-list`, `sf-empty-state`, `sf-settings-section`, `sf-detail-panel`, `sf-card`, `sf-surface`, `sf-hero`, `sf-nav`, `sf-form`, `sf-field`, `sf-input`, `sf-select`, `sf-textarea`, `sf-check`, `sf-alert`, `sf-badge`, `sf-section-header`, `sf-kicker` |
18
+ | Native components | `sf-dialog`, `sf-popover`, `sf-tooltip`, `sf-drawer`, `sf-drawer--stack`, `sf-disclosure`, `sf-accordion`, `sf-tabs`, `sf-tab__count`, `sf-menu`, `sf-breadcrumb`, `sf-pagination`, `sf-toast-stack` |
19
19
  | Website patterns | `sf-logo-cloud`, `sf-feature`, `sf-stats`, `sf-testimonial`, `sf-pricing-grid`, `sf-price-card`, `sf-faq`, `sf-cta`, `sf-footer` |
20
20
  | Utilities | `sf-prose`, `sf-link`, `sf-link-subtle`, `sf-link-plain`, `sf-list-*`, `sf-push-*`, `sf-focus-ring`, `sf-touch-target`, `sf-skip-link`, `sr-only`, `not-sr-only` |
21
21
  | CLI | `synced-flow init`, `agents install`, `agents status`, `skill`, `add defaults`, `build`, `watch`, `lint`, `doctor`, `tokens`, `catalog`, `suggest`, `pattern`, `recipe`, `theme init`, `theme validate` |
package/docs/patterns.md CHANGED
@@ -18,6 +18,13 @@ pnpm exec synced-flow pattern scroll-viewport-sections --json
18
18
  | `scroll-viewport-with-spy` | Scroll snap panels with optional IntersectionObserver active-state notes. |
19
19
  | `native-dialog-react` | Native `<dialog>` markup plus React/Next fallback shape. |
20
20
  | `popover-drawer-layout` | Popover-backed drawer or filter panel with `sf-drawer--stack`. |
21
+ | `app-shell-layout` | Fixed header, sidebar, collapsed state, and mobile drawer app shell. |
22
+ | `row-action-menu` | Native popover row actions with destructive menu item styling. |
23
+ | `tabs-with-counts` | Pill tabs with compact count chips. |
24
+ | `data-table-actions` | Sortable header controls, clickable rows, and responsive card fallback. |
25
+ | `search-field` | Labelled native search input with icon shell and compact density. |
26
+ | `modal-form` | Native dialog layout for form body and footer actions. |
27
+ | `toast-stack` | Fixed app notification stack layout. |
21
28
  | `filter-toolbar` | Search, filters, and actions above app lists and tables. |
22
29
  | `date-filter-form` | Native date/time inputs inside filter forms. |
23
30
  | `active-filter-tags` | Visible selected filter tags with remove and clear actions. |
@@ -14,6 +14,8 @@ Use the `--sf-*` variables as the stable CSS foundation.
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
+ | 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
+ | Categorical colour | `--sf-colour-categorical-1` ... `--sf-colour-categorical-6` | Charts, stages, labels, and recurring data accents. |
17
19
  | State | `--sf-colour-success`, `--sf-colour-warning`, `--sf-colour-danger`, `--sf-colour-info` | Feedback and notices. |
18
20
  | Motion | `--sf-duration-fast`, `--sf-duration-normal`, `--sf-ease-standard` | Consistent transitions. |
19
21
  | Component | `--sf-button-*`, `--sf-card-*`, `--sf-input-*`, `--sf-alert-*` | Reusable component defaults. |
@@ -36,7 +38,7 @@ These classes are the first choice for page structure.
36
38
  | `sf-cluster` | Wrapping inline groups such as actions or tags. |
37
39
  | `sf-repel` | Space-between layout that wraps safely. |
38
40
  | `sf-toolbar` | Dense app/page toolbar that wraps actions safely. |
39
- | `sf-app-shell`, `sf-app-sidebar`, `sf-app-sidebar__brand`, `sf-app-main` | Product app shell with persistent sidebar and main workspace. |
41
+ | `sf-app-shell`, `sf-app-header`, `sf-app-sidebar`, `sf-app-sidebar__brand`, `sf-app-backdrop`, `sf-app-main` | Product app shell with optional fixed header, collapsed sidebar, mobile drawer, and main workspace. |
40
42
  | `sf-split` | Responsive two-column layout. |
41
43
  | `sf-split--reverse` | Reverses split layout visual order. |
42
44
  | `sf-auto-grid`, `sf-panel-grid`, `sf-metric-grid`, `sf-pipeline` | Responsive card, metric, and operational grids. |
@@ -62,16 +64,16 @@ These cover the common UI elements needed for a simple site.
62
64
  | `sf-nav`, `sf-nav__list`, `sf-nav__link` | Navigation basics. |
63
65
  | `sf-nav--mobile`, `sf-menu`, `sf-breadcrumb`, `sf-pagination` | Mobile, menu, breadcrumb, and paginated navigation. |
64
66
  | `sf-dialog`, `sf-dialog__header`, `sf-dialog__body`, `sf-dialog__footer` | Native `<dialog>` styling. |
65
- | `sf-popover`, `sf-tooltip`, `sf-tooltip-trigger`, `sf-menu-popover`, `sf-toast`, `sf-banner`, `sf-drawer` | Popover-backed native overlays. |
67
+ | `sf-popover`, `sf-tooltip`, `sf-tooltip-trigger`, `sf-menu-popover`, `sf-toast`, `sf-toast-stack`, `sf-banner`, `sf-drawer` | Popover-backed native overlays and app feedback stacks. |
66
68
  | `sf-disclosure`, `sf-accordion` | Native `details`/`summary` disclosure patterns. |
67
- | `sf-tabs`, `sf-tab-list`, `sf-tab`, `sf-tab-panel` | HTML/CSS-first tab styling. |
69
+ | `sf-tabs`, `sf-tab-list`, `sf-tab`, `sf-tab__count`, `sf-tab-panel` | HTML/CSS-first tab styling, including counted pill tabs. |
68
70
  | `sf-form`, `sf-fieldset`, `sf-field`, `sf-label`, `sf-help`, `sf-error` | Form structure and messaging. |
69
- | `sf-input`, `sf-select`, `sf-textarea`, `sf-check` | Form controls. |
71
+ | `sf-input`, `sf-select`, `sf-textarea`, `sf-search`, `sf-check` | Form controls, including search input shell. |
70
72
  | `sf-alert`, `sf-alert--info`, `sf-alert--success`, `sf-alert--warning`, `sf-alert--danger`, `sf-alert__title` | Notices and feedback. |
71
73
  | `sf-section-header`, `sf-kicker`, `sf-badge`, `sf-avatar` | Common marketing/content and account patterns. |
72
74
  | `sf-chart`, `sf-chart__plot`, `sf-chart__svg`, `sf-chart__legend`, `sf-meter-list`, `sf-meter` | Lightweight chart shells for app-owned SVG charts and native meter bars. |
73
75
  | `sf-progress`, `sf-skeleton` | Native progress bars and CSS-only loading placeholders for async app states. |
74
- | `sf-filter-bar`, `sf-data-table`, `sf-status`, `sf-data-list`, `sf-empty-state`, `sf-settings-section`, `sf-detail-panel` | Lean application primitives for filters, operational data, status, empty states, and settings/detail screens. |
76
+ | `sf-filter-bar`, `sf-data-table`, `sf-table-sort`, `sf-status`, `sf-data-list`, `sf-empty-state`, `sf-settings-section`, `sf-detail-panel` | Lean application primitives for filters, operational data, status, empty states, and settings/detail screens. |
75
77
 
76
78
  ## Utility Classes
77
79
 
package/docs/tokens.md CHANGED
@@ -75,6 +75,7 @@ The generated core includes enough semantic variables for common website UI:
75
75
 
76
76
  - surfaces: `--sf-colour-background`, `--sf-colour-surface`,
77
77
  `--sf-colour-surface-alt`, `--sf-colour-surface-raised`,
78
+ `--sf-colour-surface-raised-2`, `--sf-colour-surface-hover`,
78
79
  `--sf-colour-surface-inset`
79
80
  - text and links: `--sf-colour-foreground`, `--sf-colour-muted`,
80
81
  `--sf-colour-subtle`, `--sf-colour-link`, `--sf-colour-link-hover`
@@ -82,8 +83,11 @@ The generated core includes enough semantic variables for common website UI:
82
83
  `--sf-colour-primary-foreground`, `--sf-colour-primary-soft`
83
84
  - state feedback: `--sf-colour-success`, `--sf-colour-warning`,
84
85
  `--sf-colour-danger`, `--sf-colour-info`, plus matching soft variants
85
- - structure: `--sf-colour-border`, `--sf-colour-border-strong`,
86
+ - structure: `--sf-colour-border-subtle`, `--sf-colour-border`,
87
+ `--sf-colour-border-strong`, `--sf-colour-backdrop`,
86
88
  `--sf-radius-control`, `--sf-radius-panel`, `--sf-shadow-*`
89
+ - data accents: `--sf-colour-categorical-1` through
90
+ `--sf-colour-categorical-6`
87
91
 
88
92
  For class-level usage, see [System primitives](system-primitives.md).
89
93
 
package/layout.css CHANGED
@@ -95,11 +95,47 @@
95
95
  }
96
96
 
97
97
  .sf-app-shell {
98
+ --sf-app-header-height: 4rem;
99
+ --sf-app-sidebar-collapsed-width: 4.75rem;
98
100
  background: var(--sf-colour-background);
99
101
  color: var(--sf-colour-foreground);
100
102
  display: grid;
101
103
  grid-template-columns: minmax(14rem, var(--sf-app-sidebar-width, 16rem)) minmax(0, 1fr);
102
104
  min-block-size: 100dvh;
105
+ position: relative;
106
+ transition: grid-template-columns var(--sf-duration-normal) var(--sf-ease-standard);
107
+ }
108
+
109
+ .sf-app-header {
110
+ align-items: center;
111
+ background: var(--sf-colour-surface);
112
+ border-block-end: 1px solid var(--sf-colour-border);
113
+ display: flex;
114
+ gap: var(--sf-space-s);
115
+ grid-column: 1 / -1;
116
+ justify-content: space-between;
117
+ min-block-size: var(--sf-app-header-height);
118
+ padding-inline: var(--sf-space-s-m);
119
+ z-index: 20;
120
+ }
121
+
122
+ .sf-app-shell[data-layout~="fixed-header"] {
123
+ grid-template-rows: var(--sf-app-header-height) minmax(0, 1fr);
124
+ }
125
+
126
+ .sf-app-shell[data-layout~="fixed-header"] .sf-app-header {
127
+ inset-block-start: 0;
128
+ position: sticky;
129
+ }
130
+
131
+ .sf-app-shell[data-layout~="fixed-header"] .sf-app-sidebar {
132
+ inset-block-start: var(--sf-app-header-height);
133
+ max-block-size: calc(100dvh - var(--sf-app-header-height));
134
+ min-block-size: calc(100dvh - var(--sf-app-header-height));
135
+ }
136
+
137
+ .sf-app-shell[data-collapsed="true"] {
138
+ grid-template-columns: var(--sf-app-sidebar-collapsed-width) minmax(0, 1fr);
103
139
  }
104
140
 
105
141
  .sf-app-sidebar {
@@ -115,6 +151,7 @@
115
151
  padding: var(--sf-space-s);
116
152
  position: sticky;
117
153
  inset-block-start: 0;
154
+ transition: transform var(--sf-duration-normal) var(--sf-ease-standard), inline-size var(--sf-duration-normal) var(--sf-ease-standard);
118
155
  }
119
156
 
120
157
  .sf-app-sidebar__brand {
@@ -134,6 +171,58 @@
134
171
  padding: var(--sf-app-main-padding, var(--sf-space-m-l));
135
172
  }
136
173
 
174
+ .sf-app-shell[data-collapsed="true"] .sf-app-sidebar {
175
+ overflow-x: hidden;
176
+ }
177
+
178
+ .sf-app-shell[data-collapsed="true"] .sf-app-sidebar__brand,
179
+ .sf-app-shell[data-collapsed="true"] .sf-app-sidebar .sf-nav__link {
180
+ justify-content: center;
181
+ }
182
+
183
+ .sf-app-shell[data-collapsed="true"] [data-hide-collapsed="true"] {
184
+ display: none;
185
+ }
186
+
187
+ .sf-app-backdrop {
188
+ display: none;
189
+ }
190
+
191
+ @media (max-width: 48rem) {
192
+ .sf-app-shell[data-mobile="drawer"] {
193
+ display: block;
194
+ }
195
+
196
+ .sf-app-shell[data-mobile="drawer"] .sf-app-header {
197
+ position: sticky;
198
+ inset-block-start: 0;
199
+ }
200
+
201
+ .sf-app-shell[data-mobile="drawer"] .sf-app-sidebar {
202
+ box-shadow: var(--sf-shadow-xl);
203
+ inline-size: min(18rem, calc(100vw - var(--sf-space-l)));
204
+ inset-block: 0;
205
+ inset-inline-start: 0;
206
+ max-block-size: none;
207
+ min-block-size: 100dvh;
208
+ position: fixed;
209
+ transform: translateX(-100%);
210
+ z-index: 30;
211
+ }
212
+
213
+ .sf-app-shell[data-mobile="drawer"][data-sidebar-open="true"] .sf-app-sidebar {
214
+ transform: translateX(0);
215
+ }
216
+
217
+ .sf-app-shell[data-mobile="drawer"][data-sidebar-open="true"] .sf-app-backdrop {
218
+ background: var(--sf-colour-backdrop);
219
+ display: block;
220
+ inset: 0;
221
+ position: fixed;
222
+ z-index: 25;
223
+ }
224
+ }
225
+
137
226
  .sf-grid {
138
227
  display: grid;
139
228
  gap: var(--sf-grid-gap, var(--sf-gutter));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syncedco/flow",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "AI-native fluid CSS design system for brand-consistent websites and agency builds.",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.2.2",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: synced-flow
3
- description: Use Synced Flow to set up and build modern fluid CSS systems with the CLI, tokens, presets, WordPress support, and source-scanned utility CSS.
3
+ description: Use Synced Flow to set up and build modern fluid CSS systems with the CLI, tokens, presets, app UI primitives, WordPress support, and source-scanned utility CSS.
4
4
  ---
5
5
 
6
6
  # Synced Flow
@@ -87,12 +87,25 @@ intact. Leave out `utilities.css` unless static helpers such as `sf-text-*`,
87
87
 
88
88
  - Prefer primitives first: `sf-container`, `sf-section`, `sf-stack`,
89
89
  `sf-cluster`, `sf-repel`, `sf-auto-grid`, `sf-split`, `sf-sidebar`,
90
- `sf-frame`, and `sf-flow`.
90
+ `sf-app-shell`, `sf-app-header`, `sf-app-sidebar`, `sf-app-main`,
91
+ `sf-app-backdrop`, `sf-frame`, and `sf-flow`.
91
92
  - Prefer component primitives for common UI: `sf-button`, `sf-card`,
92
- `sf-badge`, `sf-field`, and `sf-input`.
93
- - Use `saas-landing` for public SaaS marketing pages. Use `saas-dashboard` for
94
- authenticated app UI, admin panels, portals, CRMs, analytics dashboards,
95
- metrics, tables, account menus, and login state.
93
+ `sf-badge`, `sf-status`, `sf-field`, `sf-input`, `sf-search`,
94
+ `sf-data-table`, `sf-table-sort`, `sf-tab__count`, and `sf-toast-stack`.
95
+ - Use `saas-landing` for public SaaS marketing pages. Use `saas-dashboard` when
96
+ a full authenticated starter is useful, but prefer neutral app patterns for
97
+ reusable UI in admin panels, portals, CRMs, analytics dashboards, internal
98
+ tools, account areas, and product workspaces.
99
+ - Use app patterns before writing project CSS:
100
+ `app-shell-layout`, `row-action-menu`, `tabs-with-counts`,
101
+ `data-table-actions`, `search-field`, `modal-form`, and `toast-stack`.
102
+ - Use app shell states instead of bespoke layout CSS: `data-layout="fixed-header"`
103
+ for fixed app headers, `data-collapsed="true"` for collapsed sidebars, and
104
+ `data-mobile="drawer"` with `data-sidebar-open="true"` for mobile drawer
105
+ navigation.
106
+ - Keep product-specific structures out of Synced Flow. Kanban boards, invoices,
107
+ proposal builders, scheduling workflows, and complex editable grids should stay
108
+ in the consuming app unless they become broadly reusable primitives.
96
109
  - Do not implement authentication logic in Synced Flow markup. Wire sessions,
97
110
  providers, permissions, and sign-out actions in the consuming app.
98
111
  - Prefer native component patterns for common interaction: `sf-dialog`,
@@ -112,6 +125,10 @@ intact. Leave out `utilities.css` unless static helpers such as `sf-text-*`,
112
125
  accessibility and explicit UI styling.
113
126
  - Put brand choices in `theme` config or theme presets rather than scattered
114
127
  one-off CSS.
128
+ - Use semantic app tokens before local overrides: `surfaceRaised2`,
129
+ `surfaceHover`, `borderSubtle`, `backdrop`, and `categorical1` through
130
+ `categorical6`. Devs may still supply valid CSS colour formats such as hex,
131
+ RGB, HSL, or OKLCH in their own theme config.
115
132
  - Use modern CSS techniques already in the system: cascade layers, custom
116
133
  properties, logical properties, `clamp()`, OKLCH colour, container-aware
117
134
  layout, and `prefers-reduced-motion`.
package/src/tokens.mjs CHANGED
@@ -75,9 +75,13 @@ export const semanticTokens = {
75
75
  surface: 'var(--sf-colour-neutral-0)',
76
76
  surfaceAlt: 'var(--sf-colour-neutral-100)',
77
77
  surfaceRaised: 'var(--sf-colour-neutral-0)',
78
+ surfaceRaised2: 'var(--sf-colour-neutral-50)',
79
+ surfaceHover: 'var(--sf-colour-neutral-100)',
78
80
  surfaceInset: 'var(--sf-colour-neutral-100)',
81
+ borderSubtle: 'oklch(18% 0.026 250 / 0.08)',
79
82
  border: 'oklch(18% 0.026 250 / 0.12)',
80
83
  borderStrong: 'oklch(18% 0.026 250 / 0.22)',
84
+ backdrop: 'oklch(0% 0 0 / 0.48)',
81
85
  primary: 'var(--sf-colour-orange-600)',
82
86
  primaryHover: 'var(--sf-colour-orange-700)',
83
87
  primaryForeground: 'var(--sf-colour-neutral-0)',
@@ -99,6 +103,12 @@ export const semanticTokens = {
99
103
  info: 'var(--sf-colour-cyan-600)',
100
104
  infoForeground: 'var(--sf-colour-neutral-0)',
101
105
  infoSoft: 'oklch(70% 0.12 205 / 0.14)',
106
+ categorical1: 'var(--sf-colour-cyan-600)',
107
+ categorical2: 'var(--sf-colour-green-600)',
108
+ categorical3: 'var(--sf-colour-amber-500)',
109
+ categorical4: 'var(--sf-colour-orange-600)',
110
+ categorical5: 'var(--sf-colour-red-600)',
111
+ categorical6: 'oklch(58% 0.15 292)',
102
112
  ring: 'var(--sf-colour-orange-600)',
103
113
  },
104
114
  }