@syncedco/flow 0.1.4 → 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.
@@ -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
  }
package/styles.css CHANGED
@@ -18,6 +18,7 @@
18
18
  --sf-step-6: clamp(2.986rem, 2.5863rem + 1.7763vw, 4.2915rem);
19
19
  --sf-step-7: clamp(3.5832rem, 3.0379rem + 2.4235vw, 5.3644rem);
20
20
  --sf-step-8: clamp(4.2998rem, 3.5634rem + 3.2731vw, 6.7055rem);
21
+ --sf-type-caption-sm: var(--sf-step--2);
21
22
  --sf-type-caption: var(--sf-step--1);
22
23
  --sf-type-body: var(--sf-step-0);
23
24
  --sf-type-lead: var(--sf-step-1);
@@ -107,9 +108,13 @@
107
108
  --sf-colour-surface: var(--sf-colour-neutral-0);
108
109
  --sf-colour-surface-alt: var(--sf-colour-neutral-100);
109
110
  --sf-colour-surface-raised: var(--sf-colour-neutral-0);
111
+ --sf-colour-surface-raised-2: var(--sf-colour-neutral-50);
112
+ --sf-colour-surface-hover: var(--sf-colour-neutral-100);
110
113
  --sf-colour-surface-inset: var(--sf-colour-neutral-100);
114
+ --sf-colour-border-subtle: oklch(18% 0.026 250 / 0.08);
111
115
  --sf-colour-border: oklch(18% 0.026 250 / 0.12);
112
116
  --sf-colour-border-strong: oklch(18% 0.026 250 / 0.22);
117
+ --sf-colour-backdrop: oklch(0% 0 0 / 0.48);
113
118
  --sf-colour-primary: var(--sf-colour-orange-600);
114
119
  --sf-colour-primary-hover: var(--sf-colour-orange-700);
115
120
  --sf-colour-primary-foreground: var(--sf-colour-neutral-0);
@@ -131,6 +136,12 @@
131
136
  --sf-colour-info: var(--sf-colour-cyan-600);
132
137
  --sf-colour-info-foreground: var(--sf-colour-neutral-0);
133
138
  --sf-colour-info-soft: oklch(70% 0.12 205 / 0.14);
139
+ --sf-colour-categorical-1: var(--sf-colour-cyan-600);
140
+ --sf-colour-categorical-2: var(--sf-colour-green-600);
141
+ --sf-colour-categorical-3: var(--sf-colour-amber-500);
142
+ --sf-colour-categorical-4: var(--sf-colour-orange-600);
143
+ --sf-colour-categorical-5: var(--sf-colour-red-600);
144
+ --sf-colour-categorical-6: oklch(58% 0.15 292);
134
145
  --sf-colour-ring: var(--sf-colour-orange-600);
135
146
 
136
147
  --sf-shadow-sm: 0 0.125rem 0.375rem oklch(0% 0 0 / 0.08);
@@ -172,9 +183,13 @@
172
183
  --sf-colour-surface: var(--sf-colour-neutral-0);
173
184
  --sf-colour-surface-alt: var(--sf-colour-neutral-100);
174
185
  --sf-colour-surface-raised: var(--sf-colour-neutral-0);
186
+ --sf-colour-surface-raised-2: var(--sf-colour-neutral-50);
187
+ --sf-colour-surface-hover: var(--sf-colour-neutral-100);
175
188
  --sf-colour-surface-inset: var(--sf-colour-neutral-100);
189
+ --sf-colour-border-subtle: oklch(18% 0.026 250 / 0.08);
176
190
  --sf-colour-border: oklch(18% 0.026 250 / 0.12);
177
191
  --sf-colour-border-strong: oklch(18% 0.026 250 / 0.22);
192
+ --sf-colour-backdrop: oklch(0% 0 0 / 0.48);
178
193
  --sf-colour-link: var(--sf-colour-orange-700);
179
194
  --sf-colour-link-hover: var(--sf-colour-orange-600);
180
195
  }
@@ -188,9 +203,13 @@
188
203
  --sf-colour-surface: var(--sf-colour-neutral-900);
189
204
  --sf-colour-surface-alt: var(--sf-colour-neutral-850);
190
205
  --sf-colour-surface-raised: var(--sf-colour-neutral-850);
206
+ --sf-colour-surface-raised-2: var(--sf-colour-neutral-800);
207
+ --sf-colour-surface-hover: color-mix(in oklch, var(--sf-colour-neutral-850) 76%, white);
191
208
  --sf-colour-surface-inset: var(--sf-colour-neutral-950);
209
+ --sf-colour-border-subtle: oklch(100% 0 0 / 0.08);
192
210
  --sf-colour-border: oklch(100% 0 0 / 0.14);
193
211
  --sf-colour-border-strong: oklch(100% 0 0 / 0.24);
212
+ --sf-colour-backdrop: oklch(0% 0 0 / 0.64);
194
213
  --sf-colour-link: var(--sf-colour-orange-500);
195
214
  --sf-colour-link-hover: oklch(80% 0.14 50);
196
215
  }
@@ -398,11 +417,47 @@
398
417
  }
399
418
 
400
419
  .sf-app-shell {
420
+ --sf-app-header-height: 4rem;
421
+ --sf-app-sidebar-collapsed-width: 4.75rem;
401
422
  background: var(--sf-colour-background);
402
423
  color: var(--sf-colour-foreground);
403
424
  display: grid;
404
425
  grid-template-columns: minmax(14rem, var(--sf-app-sidebar-width, 16rem)) minmax(0, 1fr);
405
426
  min-block-size: 100dvh;
427
+ position: relative;
428
+ transition: grid-template-columns var(--sf-duration-normal) var(--sf-ease-standard);
429
+ }
430
+
431
+ .sf-app-header {
432
+ align-items: center;
433
+ background: var(--sf-colour-surface);
434
+ border-block-end: 1px solid var(--sf-colour-border);
435
+ display: flex;
436
+ gap: var(--sf-space-s);
437
+ grid-column: 1 / -1;
438
+ justify-content: space-between;
439
+ min-block-size: var(--sf-app-header-height);
440
+ padding-inline: var(--sf-space-s-m);
441
+ z-index: 20;
442
+ }
443
+
444
+ .sf-app-shell[data-layout~="fixed-header"] {
445
+ grid-template-rows: var(--sf-app-header-height) minmax(0, 1fr);
446
+ }
447
+
448
+ .sf-app-shell[data-layout~="fixed-header"] .sf-app-header {
449
+ inset-block-start: 0;
450
+ position: sticky;
451
+ }
452
+
453
+ .sf-app-shell[data-layout~="fixed-header"] .sf-app-sidebar {
454
+ inset-block-start: var(--sf-app-header-height);
455
+ max-block-size: calc(100dvh - var(--sf-app-header-height));
456
+ min-block-size: calc(100dvh - var(--sf-app-header-height));
457
+ }
458
+
459
+ .sf-app-shell[data-collapsed="true"] {
460
+ grid-template-columns: var(--sf-app-sidebar-collapsed-width) minmax(0, 1fr);
406
461
  }
407
462
 
408
463
  .sf-app-sidebar {
@@ -418,6 +473,7 @@
418
473
  padding: var(--sf-space-s);
419
474
  position: sticky;
420
475
  inset-block-start: 0;
476
+ transition: transform var(--sf-duration-normal) var(--sf-ease-standard), inline-size var(--sf-duration-normal) var(--sf-ease-standard);
421
477
  }
422
478
 
423
479
  .sf-app-sidebar__brand {
@@ -437,6 +493,58 @@
437
493
  padding: var(--sf-app-main-padding, var(--sf-space-m-l));
438
494
  }
439
495
 
496
+ .sf-app-shell[data-collapsed="true"] .sf-app-sidebar {
497
+ overflow-x: hidden;
498
+ }
499
+
500
+ .sf-app-shell[data-collapsed="true"] .sf-app-sidebar__brand,
501
+ .sf-app-shell[data-collapsed="true"] .sf-app-sidebar .sf-nav__link {
502
+ justify-content: center;
503
+ }
504
+
505
+ .sf-app-shell[data-collapsed="true"] [data-hide-collapsed="true"] {
506
+ display: none;
507
+ }
508
+
509
+ .sf-app-backdrop {
510
+ display: none;
511
+ }
512
+
513
+ @media (max-width: 48rem) {
514
+ .sf-app-shell[data-mobile="drawer"] {
515
+ display: block;
516
+ }
517
+
518
+ .sf-app-shell[data-mobile="drawer"] .sf-app-header {
519
+ position: sticky;
520
+ inset-block-start: 0;
521
+ }
522
+
523
+ .sf-app-shell[data-mobile="drawer"] .sf-app-sidebar {
524
+ box-shadow: var(--sf-shadow-xl);
525
+ inline-size: min(18rem, calc(100vw - var(--sf-space-l)));
526
+ inset-block: 0;
527
+ inset-inline-start: 0;
528
+ max-block-size: none;
529
+ min-block-size: 100dvh;
530
+ position: fixed;
531
+ transform: translateX(-100%);
532
+ z-index: 30;
533
+ }
534
+
535
+ .sf-app-shell[data-mobile="drawer"][data-sidebar-open="true"] .sf-app-sidebar {
536
+ transform: translateX(0);
537
+ }
538
+
539
+ .sf-app-shell[data-mobile="drawer"][data-sidebar-open="true"] .sf-app-backdrop {
540
+ background: var(--sf-colour-backdrop);
541
+ display: block;
542
+ inset: 0;
543
+ position: fixed;
544
+ z-index: 25;
545
+ }
546
+ }
547
+
440
548
  .sf-grid {
441
549
  display: grid;
442
550
  gap: var(--sf-grid-gap, var(--sf-gutter));
@@ -1468,6 +1576,257 @@
1468
1576
  overflow: hidden;
1469
1577
  }
1470
1578
 
1579
+ .sf-progress {
1580
+ accent-color: var(--sf-progress-colour, var(--sf-colour-primary));
1581
+ block-size: var(--sf-progress-size, .625rem);
1582
+ inline-size: 100%;
1583
+ }
1584
+
1585
+ .sf-progress::-webkit-progress-bar {
1586
+ background: var(--sf-colour-surface-alt);
1587
+ border-radius: var(--sf-radius-full);
1588
+ }
1589
+
1590
+ .sf-progress::-webkit-progress-value,
1591
+ .sf-progress::-moz-progress-bar {
1592
+ background: var(--sf-progress-colour, var(--sf-colour-primary));
1593
+ border-radius: var(--sf-radius-full);
1594
+ }
1595
+
1596
+ .sf-skeleton {
1597
+ background: color-mix(in oklch, var(--sf-colour-surface-alt) 82%, var(--sf-colour-foreground));
1598
+ border-radius: var(--sf-skeleton-radius, var(--sf-radius-md));
1599
+ color: transparent;
1600
+ min-block-size: var(--sf-skeleton-block-size, 1em);
1601
+ }
1602
+
1603
+ @media (prefers-reduced-motion: no-preference) {
1604
+ .sf-skeleton {
1605
+ animation: sf-skeleton-pulse 1.4s var(--sf-ease-standard) infinite alternate;
1606
+ }
1607
+ }
1608
+
1609
+ @keyframes sf-skeleton-pulse {
1610
+ to { opacity: .62; }
1611
+ }
1612
+
1613
+ .sf-filter-bar {
1614
+ align-items: end;
1615
+ background: var(--sf-colour-surface);
1616
+ border: 1px solid var(--sf-colour-border);
1617
+ border-radius: var(--sf-radius-panel);
1618
+ display: flex;
1619
+ flex-wrap: wrap;
1620
+ gap: var(--sf-space-xs);
1621
+ padding: var(--sf-space-s);
1622
+ }
1623
+
1624
+ .sf-filter-bar > :where(.sf-field, form, label) {
1625
+ flex: 1 1 min(100%, 14rem);
1626
+ }
1627
+
1628
+ .sf-filter-bar > :where(.sf-button, button, a) {
1629
+ flex: 0 0 auto;
1630
+ }
1631
+
1632
+ .sf-data-table {
1633
+ border-collapse: collapse;
1634
+ inline-size: 100%;
1635
+ min-inline-size: var(--sf-data-table-min, 44rem);
1636
+ }
1637
+
1638
+ .sf-data-table :where(th, td) {
1639
+ border-block-end: 1px solid var(--sf-colour-border);
1640
+ padding-block: var(--sf-data-table-padding-block, var(--sf-space-xs));
1641
+ padding-inline: var(--sf-data-table-padding-inline, var(--sf-space-s));
1642
+ text-align: start;
1643
+ vertical-align: middle;
1644
+ }
1645
+
1646
+ .sf-data-table :where(th) {
1647
+ background: var(--sf-colour-surface-alt);
1648
+ color: var(--sf-colour-muted);
1649
+ font-size: var(--sf-step--1);
1650
+ font-weight: 800;
1651
+ }
1652
+
1653
+ .sf-data-table :where(tbody tr:hover) {
1654
+ background: color-mix(in oklch, var(--sf-colour-primary) 6%, transparent);
1655
+ }
1656
+
1657
+ .sf-data-table[data-density="compact"] {
1658
+ --sf-data-table-padding-block: var(--sf-space-2xs);
1659
+ --sf-data-table-padding-inline: var(--sf-space-xs);
1660
+ }
1661
+
1662
+ .sf-data-table :where(th button, .sf-table-sort) {
1663
+ align-items: center;
1664
+ color: inherit;
1665
+ display: inline-flex;
1666
+ font: inherit;
1667
+ font-weight: inherit;
1668
+ gap: var(--sf-space-3xs);
1669
+ padding: 0;
1670
+ }
1671
+
1672
+ .sf-data-table :where(th button, .sf-table-sort)[aria-sort]::after {
1673
+ color: var(--sf-colour-subtle);
1674
+ content: "\2195";
1675
+ font-size: var(--sf-step--2);
1676
+ }
1677
+
1678
+ .sf-data-table :where(th button, .sf-table-sort)[aria-sort="ascending"]::after { content: "\2191"; }
1679
+ .sf-data-table :where(th button, .sf-table-sort)[aria-sort="descending"]::after { content: "\2193"; }
1680
+
1681
+ .sf-data-table tr[data-clickable="true"] {
1682
+ cursor: pointer;
1683
+ }
1684
+
1685
+ .sf-data-table tr[data-clickable="true"]:focus-within {
1686
+ outline: 0.125rem solid var(--sf-colour-ring);
1687
+ outline-offset: -0.125rem;
1688
+ }
1689
+
1690
+ @container (max-width: 38rem) {
1691
+ .sf-table-wrap[data-view="cards"] {
1692
+ border: 0;
1693
+ overflow: visible;
1694
+ }
1695
+
1696
+ .sf-table-wrap[data-view="cards"] .sf-data-table,
1697
+ .sf-table-wrap[data-view="cards"] .sf-data-table :where(thead, tbody, tr, th, td) {
1698
+ display: block;
1699
+ min-inline-size: 0;
1700
+ }
1701
+
1702
+ .sf-table-wrap[data-view="cards"] .sf-data-table thead {
1703
+ display: none;
1704
+ }
1705
+
1706
+ .sf-table-wrap[data-view="cards"] .sf-data-table tr {
1707
+ background: var(--sf-colour-surface);
1708
+ border: 1px solid var(--sf-colour-border);
1709
+ border-radius: var(--sf-radius-panel);
1710
+ margin-block-end: var(--sf-space-xs);
1711
+ padding: var(--sf-space-xs);
1712
+ }
1713
+
1714
+ .sf-table-wrap[data-view="cards"] .sf-data-table td {
1715
+ align-items: center;
1716
+ border: 0;
1717
+ display: flex;
1718
+ gap: var(--sf-space-s);
1719
+ justify-content: space-between;
1720
+ padding-block: var(--sf-space-2xs);
1721
+ padding-inline: 0;
1722
+ }
1723
+
1724
+ .sf-table-wrap[data-view="cards"] .sf-data-table td::before {
1725
+ color: var(--sf-colour-muted);
1726
+ content: attr(data-label);
1727
+ font-size: var(--sf-step--1);
1728
+ font-weight: 800;
1729
+ }
1730
+ }
1731
+
1732
+ .sf-status {
1733
+ align-items: center;
1734
+ background: var(--sf-status-bg, var(--sf-colour-surface-alt));
1735
+ border: 1px solid var(--sf-status-border, var(--sf-colour-border));
1736
+ border-radius: var(--sf-radius-full);
1737
+ color: var(--sf-status-colour, var(--sf-colour-foreground));
1738
+ display: inline-flex;
1739
+ font-size: var(--sf-step--1);
1740
+ font-weight: 700;
1741
+ gap: var(--sf-space-3xs);
1742
+ line-height: 1;
1743
+ min-block-size: 1.75rem;
1744
+ padding-inline: .7em;
1745
+ }
1746
+
1747
+ .sf-status[data-size="sm"] {
1748
+ font-size: var(--sf-step--2);
1749
+ min-block-size: 1.35rem;
1750
+ padding-inline: .55em;
1751
+ }
1752
+
1753
+ .sf-status::before {
1754
+ background: currentColor;
1755
+ block-size: .42em;
1756
+ border-radius: var(--sf-radius-full);
1757
+ content: "";
1758
+ inline-size: .42em;
1759
+ }
1760
+
1761
+ .sf-status[data-tone="success"] {
1762
+ --sf-status-bg: var(--sf-colour-success-soft);
1763
+ --sf-status-border: color-mix(in oklch, var(--sf-colour-success) 34%, transparent);
1764
+ --sf-status-colour: color-mix(in oklch, var(--sf-colour-success) 76%, var(--sf-colour-foreground));
1765
+ }
1766
+
1767
+ .sf-status[data-tone="warning"] {
1768
+ --sf-status-bg: var(--sf-colour-warning-soft);
1769
+ --sf-status-border: color-mix(in oklch, var(--sf-colour-warning) 38%, transparent);
1770
+ --sf-status-colour: color-mix(in oklch, var(--sf-colour-warning) 70%, var(--sf-colour-foreground));
1771
+ }
1772
+
1773
+ .sf-status[data-tone="danger"] {
1774
+ --sf-status-bg: var(--sf-colour-danger-soft);
1775
+ --sf-status-border: color-mix(in oklch, var(--sf-colour-danger) 34%, transparent);
1776
+ --sf-status-colour: color-mix(in oklch, var(--sf-colour-danger) 76%, var(--sf-colour-foreground));
1777
+ }
1778
+
1779
+ .sf-status[data-tone="info"] {
1780
+ --sf-status-bg: var(--sf-colour-info-soft);
1781
+ --sf-status-border: color-mix(in oklch, var(--sf-colour-info) 34%, transparent);
1782
+ --sf-status-colour: color-mix(in oklch, var(--sf-colour-info) 76%, var(--sf-colour-foreground));
1783
+ }
1784
+
1785
+ .sf-data-list {
1786
+ display: grid;
1787
+ }
1788
+
1789
+ .sf-data-list > * {
1790
+ align-items: center;
1791
+ border-block-end: 1px solid var(--sf-colour-border);
1792
+ display: flex;
1793
+ flex-wrap: wrap;
1794
+ gap: var(--sf-space-xs);
1795
+ justify-content: space-between;
1796
+ padding-block: var(--sf-space-xs);
1797
+ }
1798
+
1799
+ .sf-data-list > :last-child {
1800
+ border-block-end: 0;
1801
+ }
1802
+
1803
+ .sf-empty-state {
1804
+ align-items: center;
1805
+ background: var(--sf-colour-surface-alt);
1806
+ border: 1px dashed var(--sf-colour-border-strong);
1807
+ border-radius: var(--sf-radius-panel);
1808
+ color: var(--sf-colour-muted);
1809
+ display: grid;
1810
+ gap: var(--sf-space-xs);
1811
+ justify-items: center;
1812
+ padding: var(--sf-space-l);
1813
+ text-align: center;
1814
+ }
1815
+
1816
+ .sf-settings-section,
1817
+ .sf-detail-panel {
1818
+ background: var(--sf-colour-surface);
1819
+ border: 1px solid var(--sf-colour-border);
1820
+ border-radius: var(--sf-radius-panel);
1821
+ display: grid;
1822
+ gap: var(--sf-space-s);
1823
+ padding: var(--sf-space-m);
1824
+ }
1825
+
1826
+ .sf-detail-panel {
1827
+ background: var(--sf-colour-surface-alt);
1828
+ }
1829
+
1471
1830
  .sf-nav {
1472
1831
  align-items: center;
1473
1832
  display: flex;
@@ -1546,6 +1905,13 @@
1546
1905
  gap: var(--sf-form-gap, var(--sf-space-s-m));
1547
1906
  }
1548
1907
 
1908
+ .sf-form[data-density="compact"],
1909
+ .sf-field[data-density="compact"] {
1910
+ --sf-form-gap: var(--sf-space-xs);
1911
+ --sf-input-block-size: 2.25rem;
1912
+ --sf-input-padding-inline: var(--sf-space-xs);
1913
+ }
1914
+
1549
1915
  .sf-fieldset {
1550
1916
  border: 1px solid var(--sf-colour-border);
1551
1917
  border-radius: var(--sf-radius-panel);
@@ -1611,6 +1977,45 @@
1611
1977
  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);
1612
1978
  }
1613
1979
 
1980
+ .sf-search {
1981
+ position: relative;
1982
+ }
1983
+
1984
+ .sf-search::before {
1985
+ block-size: .75em;
1986
+ border: 0.125em solid currentColor;
1987
+ border-radius: var(--sf-radius-full);
1988
+ color: var(--sf-colour-subtle);
1989
+ content: "";
1990
+ inline-size: .75em;
1991
+ inset-block-start: 50%;
1992
+ inset-inline-start: var(--sf-space-xs);
1993
+ line-height: 1;
1994
+ position: absolute;
1995
+ transform: translateY(-58%);
1996
+ }
1997
+
1998
+ .sf-search::after {
1999
+ background: var(--sf-colour-subtle);
2000
+ block-size: .125em;
2001
+ content: "";
2002
+ inline-size: .45em;
2003
+ inset-block-start: 50%;
2004
+ inset-inline-start: calc(var(--sf-space-xs) + .58em);
2005
+ position: absolute;
2006
+ transform: translateY(120%) rotate(45deg);
2007
+ transform-origin: inline-start center;
2008
+ }
2009
+
2010
+ .sf-search > .sf-input {
2011
+ padding-inline-start: var(--sf-space-l);
2012
+ }
2013
+
2014
+ .sf-help[data-density="compact"],
2015
+ .sf-error[data-density="compact"] {
2016
+ font-size: var(--sf-step--2);
2017
+ }
2018
+
1614
2019
  .sf-textarea {
1615
2020
  min-block-size: var(--sf-textarea-block-size, 8rem);
1616
2021
  padding-block: var(--sf-space-xs);
@@ -1731,7 +2136,7 @@
1731
2136
  .sf-dialog::backdrop,
1732
2137
  .sf-popover::backdrop,
1733
2138
  .sf-drawer::backdrop {
1734
- background: oklch(0% 0 0 / 0.48);
2139
+ background: var(--sf-colour-backdrop);
1735
2140
  }
1736
2141
 
1737
2142
  .sf-dialog__header,
@@ -1757,6 +2162,11 @@
1757
2162
  justify-content: flex-end;
1758
2163
  }
1759
2164
 
2165
+ .sf-dialog[data-layout="form"] .sf-dialog__body {
2166
+ display: grid;
2167
+ gap: var(--sf-space-s);
2168
+ }
2169
+
1760
2170
  .sf-popover,
1761
2171
  .sf-menu-popover,
1762
2172
  .sf-tooltip,
@@ -1810,6 +2220,16 @@
1810
2220
  background: var(--sf-colour-surface-alt);
1811
2221
  }
1812
2222
 
2223
+ .sf-menu :where(a, button)[data-variant="danger"],
2224
+ .sf-menu-popover :where(a, button)[data-variant="danger"] {
2225
+ color: var(--sf-colour-danger);
2226
+ }
2227
+
2228
+ .sf-menu :where(a, button)[data-variant="danger"]:hover,
2229
+ .sf-menu-popover :where(a, button)[data-variant="danger"]:hover {
2230
+ background: var(--sf-colour-danger-soft);
2231
+ }
2232
+
1813
2233
  .sf-tooltip {
1814
2234
  border-radius: var(--sf-radius-md);
1815
2235
  box-shadow: var(--sf-shadow-md);
@@ -1836,6 +2256,16 @@
1836
2256
  top: var(--sf-space-s);
1837
2257
  }
1838
2258
 
2259
+ .sf-toast-stack {
2260
+ display: grid;
2261
+ gap: var(--sf-space-xs);
2262
+ inline-size: min(100% - (var(--sf-gutter) * 2), var(--sf-toast-width, 24rem));
2263
+ inset-block-start: var(--sf-space-s);
2264
+ inset-inline-end: var(--sf-space-s);
2265
+ position: fixed;
2266
+ z-index: 50;
2267
+ }
2268
+
1839
2269
  .sf-banner {
1840
2270
  block-size: auto;
1841
2271
  border-radius: var(--sf-radius-lg);
@@ -1949,14 +2379,21 @@
1949
2379
 
1950
2380
  .sf-tab {
1951
2381
  border-block-end: 0.125rem solid transparent;
2382
+ border-radius: var(--sf-radius-full);
1952
2383
  color: var(--sf-colour-muted);
1953
2384
  display: inline-flex;
2385
+ gap: var(--sf-space-2xs);
1954
2386
  font-weight: 800;
1955
2387
  padding-block: var(--sf-space-xs);
1956
2388
  padding-inline: var(--sf-space-s);
1957
2389
  text-decoration: none;
1958
2390
  }
1959
2391
 
2392
+ .sf-tab[data-variant="pill"] {
2393
+ border: 1px solid transparent;
2394
+ padding-block: var(--sf-space-2xs);
2395
+ }
2396
+
1960
2397
  .sf-tab:hover,
1961
2398
  .sf-tab[aria-selected="true"],
1962
2399
  .sf-tab[data-state="active"] {
@@ -1964,6 +2401,24 @@
1964
2401
  color: var(--sf-colour-foreground);
1965
2402
  }
1966
2403
 
2404
+ .sf-tab[data-variant="pill"]:hover,
2405
+ .sf-tab[data-variant="pill"][aria-selected="true"],
2406
+ .sf-tab[data-variant="pill"][data-state="active"] {
2407
+ background: var(--sf-colour-primary-soft);
2408
+ border-color: var(--sf-colour-primary-soft-border);
2409
+ color: var(--sf-colour-primary);
2410
+ }
2411
+
2412
+ .sf-tab__count {
2413
+ background: var(--sf-colour-surface-alt);
2414
+ border: 1px solid var(--sf-colour-border);
2415
+ border-radius: var(--sf-radius-full);
2416
+ color: var(--sf-colour-muted);
2417
+ font-size: var(--sf-step--2);
2418
+ line-height: 1;
2419
+ padding: .25em .55em;
2420
+ }
2421
+
1967
2422
  .sf-tab-panel {
1968
2423
  background: var(--sf-colour-surface);
1969
2424
  border: 1px solid var(--sf-colour-border);
@@ -2140,6 +2595,7 @@
2140
2595
  .sf-table-wrap {
2141
2596
  border: 1px solid var(--sf-colour-border);
2142
2597
  border-radius: var(--sf-radius-panel);
2598
+ container-type: inline-size;
2143
2599
  overflow-x: auto;
2144
2600
  }
2145
2601