@felloinnovations/brand-kit 0.4.0 → 0.5.1
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/AGENTS.md +198 -24
- package/CHANGELOG.md +221 -0
- package/README.md +38 -9
- package/design-system/01-how-claude-decides.md +26 -17
- package/design-system/02-principles.md +11 -6
- package/design-system/03-colour.md +135 -47
- package/design-system/04-type.md +45 -23
- package/design-system/05-space-and-layout.md +175 -52
- package/design-system/06-sizing-profiles.md +4 -4
- package/design-system/07-scrolling.md +66 -20
- package/design-system/08-choosing-components.md +50 -22
- package/design-system/09-charts.md +71 -28
- package/design-system/10-patterns.md +33 -10
- package/design-system/11-page-recipes.md +110 -23
- package/design-system/12-content.md +2 -1
- package/design-system/13-motion.md +330 -47
- package/design-system/14-accessibility.md +47 -24
- package/design-system/15-ai-experiences.md +11 -6
- package/design-system/16-creativity.md +1 -1
- package/design-system/17-decisions.md +367 -1
- package/design-system/18-checklist.md +79 -16
- package/design-system/19-public-pages.md +6 -4
- package/design-system/20-palettes.md +275 -0
- package/design-system/README.md +30 -17
- package/design-system/components/ActionList/README.md +96 -10
- package/design-system/components/ActionList/preview.html +52 -7
- package/design-system/components/ActionList/style.css +64 -0
- package/design-system/components/ActivityGrid/README.md +38 -2
- package/design-system/components/ActivityGrid/preview.html +1 -1
- package/design-system/components/ActivityGrid/style.css +35 -0
- package/design-system/components/AppShell/README.md +205 -75
- package/design-system/components/AppShell/preview.html +339 -58
- package/design-system/components/AppShell/style.css +154 -0
- package/design-system/components/Avatar/README.md +37 -10
- package/design-system/components/Avatar/preview.html +13 -4
- package/design-system/components/Avatar/style.css +14 -0
- package/design-system/components/Badge/README.md +41 -19
- package/design-system/components/Badge/preview.html +13 -10
- package/design-system/components/Badge/style.css +23 -0
- package/design-system/components/Banner/README.md +30 -9
- package/design-system/components/Banner/preview.html +1 -1
- package/design-system/components/Banner/style.css +19 -0
- package/design-system/components/Breadcrumb/README.md +58 -46
- package/design-system/components/Breadcrumb/preview.html +16 -16
- package/design-system/components/Breadcrumb/style.css +17 -0
- package/design-system/components/Button/README.md +113 -55
- package/design-system/components/Button/preview.html +98 -25
- package/design-system/components/Button/style.css +86 -0
- package/design-system/components/Card/README.md +158 -49
- package/design-system/components/Card/preview.html +141 -19
- package/design-system/components/Card/style.css +115 -0
- package/design-system/components/CardBoard/README.md +58 -37
- package/design-system/components/CardBoard/preview.html +26 -29
- package/design-system/components/ChatCard/README.md +115 -29
- package/design-system/components/ChatCard/preview.html +149 -32
- package/design-system/components/ChatCard/style.css +49 -0
- package/design-system/components/ChatShell/README.md +116 -0
- package/design-system/components/ChatShell/preview.html +201 -0
- package/design-system/components/ChatShell/style.css +86 -0
- package/design-system/components/ChatTurn/README.md +78 -35
- package/design-system/components/ChatTurn/preview.html +60 -28
- package/design-system/components/ChatTurn/style.css +50 -0
- package/design-system/components/CheckRow/README.md +141 -0
- package/design-system/components/CheckRow/preview.html +140 -0
- package/design-system/components/CheckRow/style.css +88 -0
- package/design-system/components/Checkbox/README.md +58 -22
- package/design-system/components/Checkbox/preview.html +112 -50
- package/design-system/components/Checkbox/style.css +45 -0
- package/design-system/components/Chip/README.md +78 -47
- package/design-system/components/Chip/preview.html +29 -4
- package/design-system/components/Chip/style.css +17 -0
- package/design-system/components/Combobox/README.md +44 -11
- package/design-system/components/Combobox/preview.html +35 -12
- package/design-system/components/Combobox/style.css +54 -0
- package/design-system/components/Composer/README.md +137 -35
- package/design-system/components/Composer/preview.html +115 -40
- package/design-system/components/Composer/style.css +81 -0
- package/design-system/components/ConfirmDialog/README.md +73 -52
- package/design-system/components/ConfirmDialog/preview.html +70 -26
- package/design-system/components/ConfirmDialog/style.css +5 -0
- package/design-system/components/Cover/preview.html +3 -3
- package/design-system/components/Cover/style.css +19 -0
- package/design-system/components/DateInput/README.md +77 -12
- package/design-system/components/DateInput/preview.html +27 -12
- package/design-system/components/DateInput/style.css +38 -0
- package/design-system/components/Dialog/README.md +112 -44
- package/design-system/components/Dialog/preview.html +141 -18
- package/design-system/components/Dialog/style.css +74 -0
- package/design-system/components/EmptyState/README.md +42 -12
- package/design-system/components/EmptyState/preview.html +11 -11
- package/design-system/components/EmptyState/style.css +9 -0
- package/design-system/components/EntryShell/README.md +73 -48
- package/design-system/components/EntryShell/preview.html +60 -27
- package/design-system/components/EntryShell/style.css +19 -0
- package/design-system/components/FeatureGrid/README.md +61 -39
- package/design-system/components/FeatureGrid/preview.html +2 -2
- package/design-system/components/FeatureGrid/style.css +20 -0
- package/design-system/components/FileUpload/README.md +33 -3
- package/design-system/components/FileUpload/preview.html +18 -4
- package/design-system/components/FileUpload/style.css +23 -0
- package/design-system/components/FilterBar/README.md +114 -0
- package/design-system/components/FilterBar/preview.html +82 -0
- package/design-system/components/FilterBar/style.css +22 -0
- package/design-system/components/Fold/README.md +84 -26
- package/design-system/components/Fold/preview.html +77 -34
- package/design-system/components/Fold/style.css +48 -0
- package/design-system/components/Footer/README.md +62 -39
- package/design-system/components/Footer/preview.html +5 -5
- package/design-system/components/Footer/style.css +30 -0
- package/design-system/components/FunnelBars/README.md +24 -3
- package/design-system/components/FunnelBars/preview.html +5 -5
- package/design-system/components/FunnelBars/style.css +13 -0
- package/design-system/components/GoalBars/README.md +54 -10
- package/design-system/components/GoalBars/preview.html +69 -28
- package/design-system/components/GoalBars/style.css +51 -0
- package/design-system/components/GoalTicks/README.md +74 -58
- package/design-system/components/GoalTicks/preview.html +41 -118
- package/design-system/components/GoalTicks/style.css +37 -0
- package/design-system/components/GoalTubes/README.md +29 -3
- package/design-system/components/GoalTubes/preview.html +6 -6
- package/design-system/components/GoalTubes/style.css +23 -0
- package/design-system/components/Heatmap/README.md +133 -0
- package/design-system/components/Heatmap/preview.html +168 -0
- package/design-system/components/Heatmap/style.css +119 -0
- package/design-system/components/Hero/README.md +58 -40
- package/design-system/components/Hero/preview.html +1 -1
- package/design-system/components/Hero/style.css +19 -0
- package/design-system/components/HoverCard/README.md +114 -0
- package/design-system/components/HoverCard/preview.html +107 -0
- package/design-system/components/HoverCard/style.css +59 -0
- package/design-system/components/IconButton/README.md +69 -38
- package/design-system/components/IconButton/preview.html +77 -43
- package/design-system/components/IconButton/style.css +45 -0
- package/design-system/components/Input/README.md +36 -11
- package/design-system/components/Input/preview.html +1 -1
- package/design-system/components/Input/style.css +23 -0
- package/design-system/components/KanbanBoard/README.md +93 -45
- package/design-system/components/KanbanBoard/preview.html +159 -43
- package/design-system/components/KanbanBoard/style.css +53 -0
- package/design-system/components/Kbd/README.md +25 -9
- package/design-system/components/Kbd/preview.html +6 -2
- package/design-system/components/Kbd/style.css +9 -0
- package/design-system/components/KitButton/README.md +81 -47
- package/design-system/components/KitButton/preview.html +35 -13
- package/design-system/components/KitButton/style.css +46 -0
- package/design-system/components/KitCard/README.md +75 -44
- package/design-system/components/KitCard/preview.html +18 -10
- package/design-system/components/KitCard/style.css +15 -0
- package/design-system/components/KitInput/README.md +54 -39
- package/design-system/components/KitInput/preview.html +7 -7
- package/design-system/components/KitInput/style.css +23 -0
- package/design-system/components/Leaderboard/README.md +76 -49
- package/design-system/components/Leaderboard/preview.html +61 -26
- package/design-system/components/Leaderboard/style.css +44 -0
- package/design-system/components/ListDetail/README.md +222 -0
- package/design-system/components/ListDetail/preview.html +213 -0
- package/design-system/components/ListDetail/style.css +148 -0
- package/design-system/components/LoadBar/README.md +79 -0
- package/design-system/components/LoadBar/preview.html +40 -0
- package/design-system/components/LoadBar/style.css +17 -0
- package/design-system/components/MarketingNav/README.md +76 -42
- package/design-system/components/MarketingNav/preview.html +37 -8
- package/design-system/components/MarketingNav/style.css +45 -0
- package/design-system/components/Matrix/README.md +129 -0
- package/design-system/components/Matrix/preview.html +99 -0
- package/design-system/components/Matrix/style.css +84 -0
- package/design-system/components/Menu/README.md +117 -41
- package/design-system/components/Menu/preview.html +85 -23
- package/design-system/components/Menu/style.css +61 -0
- package/design-system/components/Meter/README.md +39 -4
- package/design-system/components/Meter/preview.html +18 -1
- package/design-system/components/Meter/style.css +23 -0
- package/design-system/components/MetricTile/README.md +237 -0
- package/design-system/components/MetricTile/preview.html +241 -0
- package/design-system/components/MetricTile/style.css +129 -0
- package/design-system/components/Morph/README.md +106 -0
- package/design-system/components/Morph/preview.html +103 -0
- package/design-system/components/Morph/style.css +31 -0
- package/design-system/components/Motion/README.md +164 -0
- package/design-system/components/Motion/preview.html +2537 -0
- package/design-system/components/NumberInput/README.md +27 -8
- package/design-system/components/NumberInput/preview.html +21 -5
- package/design-system/components/NumberInput/style.css +34 -0
- package/design-system/components/PaceBar/README.md +75 -0
- package/design-system/components/PaceBar/preview.html +67 -0
- package/design-system/components/PaceBar/style.css +25 -0
- package/design-system/components/Page/README.md +57 -18
- package/design-system/components/Page/preview.html +15 -17
- package/design-system/components/Page/style.css +7 -0
- package/design-system/components/PageBar/README.md +90 -34
- package/design-system/components/PageBar/preview.html +60 -19
- package/design-system/components/PageBar/style.css +35 -0
- package/design-system/components/PageHero/README.md +87 -51
- package/design-system/components/PageHero/preview.html +35 -6
- package/design-system/components/PageHero/style.css +31 -0
- package/design-system/components/Pagination/README.md +70 -49
- package/design-system/components/Pagination/preview.html +24 -15
- package/design-system/components/Pagination/style.css +12 -0
- package/design-system/components/Panel/README.md +109 -51
- package/design-system/components/Panel/preview.html +118 -20
- package/design-system/components/Panel/style.css +51 -0
- package/design-system/components/PasswordInput/README.md +50 -18
- package/design-system/components/PasswordInput/preview.html +71 -21
- package/design-system/components/PasswordInput/style.css +31 -0
- package/design-system/components/Plate/README.md +75 -17
- package/design-system/components/Plate/preview.html +40 -7
- package/design-system/components/Plate/style.css +37 -0
- package/design-system/components/Popover/README.md +70 -49
- package/design-system/components/Popover/preview.html +44 -22
- package/design-system/components/Popover/style.css +41 -0
- package/design-system/components/PricingTable/README.md +69 -49
- package/design-system/components/PricingTable/preview.html +21 -21
- package/design-system/components/PricingTable/style.css +21 -0
- package/design-system/components/ProgressPetals/README.md +31 -3
- package/design-system/components/ProgressPetals/preview.html +14 -15
- package/design-system/components/ProgressPetals/style.css +27 -0
- package/design-system/components/README.md +151 -0
- package/design-system/components/Radio/README.md +40 -17
- package/design-system/components/Radio/preview.html +16 -1
- package/design-system/components/Radio/style.css +21 -0
- package/design-system/components/RankBar/README.md +29 -2
- package/design-system/components/RankBar/preview.html +14 -12
- package/design-system/components/RankBar/style.css +18 -0
- package/design-system/components/Ring/README.md +61 -11
- package/design-system/components/Ring/preview.html +55 -10
- package/design-system/components/Ring/style.css +49 -0
- package/design-system/components/ScoreHero/README.md +91 -0
- package/design-system/components/ScoreHero/preview.html +104 -0
- package/design-system/components/ScoreHero/style.css +32 -0
- package/design-system/components/Section/README.md +72 -33
- package/design-system/components/Section/preview.html +46 -16
- package/design-system/components/Section/style.css +18 -0
- package/design-system/components/Segmented/README.md +100 -55
- package/design-system/components/Segmented/preview.html +107 -26
- package/design-system/components/Segmented/style.css +94 -0
- package/design-system/components/SegmentedProgress/README.md +64 -0
- package/design-system/components/SegmentedProgress/preview.html +69 -0
- package/design-system/components/SegmentedProgress/style.css +11 -0
- package/design-system/components/Select/README.md +47 -12
- package/design-system/components/Select/preview.html +28 -2
- package/design-system/components/Select/style.css +22 -0
- package/design-system/components/ShareBar/README.md +69 -0
- package/design-system/components/ShareBar/preview.html +72 -0
- package/design-system/components/ShareBar/style.css +25 -0
- package/design-system/components/SideList/README.md +147 -0
- package/design-system/components/SideList/preview.html +139 -0
- package/design-system/components/SideList/style.css +94 -0
- package/design-system/components/Skeleton/README.md +53 -16
- package/design-system/components/Skeleton/preview.html +35 -23
- package/design-system/components/Skeleton/style.css +17 -0
- package/design-system/components/Slider/README.md +95 -0
- package/design-system/components/Slider/preview.html +49 -0
- package/design-system/components/Slider/style.css +13 -0
- package/design-system/components/Split/README.md +112 -0
- package/design-system/components/Split/preview.html +105 -0
- package/design-system/components/Split/style.css +30 -0
- package/design-system/components/Starter/README.md +21 -9
- package/design-system/components/Starter/preview.html +5 -5
- package/design-system/components/Stat/README.md +64 -43
- package/design-system/components/Stat/preview.html +3 -3
- package/design-system/components/Stat/style.css +15 -0
- package/design-system/components/StatTiles/README.md +88 -44
- package/design-system/components/StatTiles/preview.html +142 -71
- package/design-system/components/StatTiles/style.css +64 -0
- package/design-system/components/StatusLine/README.md +85 -0
- package/design-system/components/StatusLine/preview.html +49 -0
- package/design-system/components/StatusLine/style.css +14 -0
- package/design-system/components/StatusScreen/README.md +49 -35
- package/design-system/components/StatusScreen/preview.html +20 -5
- package/design-system/components/StatusScreen/style.css +9 -0
- package/design-system/components/StepLine/README.md +97 -0
- package/design-system/components/StepLine/preview.html +108 -0
- package/design-system/components/StepLine/style.css +62 -0
- package/design-system/components/Stepper/README.md +35 -13
- package/design-system/components/Stepper/style.css +13 -0
- package/design-system/components/StreakCalendar/README.md +22 -1
- package/design-system/components/StreakCalendar/preview.html +10 -10
- package/design-system/components/StreakCalendar/style.css +12 -0
- package/design-system/components/SuggestionChip/README.md +87 -0
- package/design-system/components/SuggestionChip/preview.html +42 -0
- package/design-system/components/SuggestionChip/style.css +20 -0
- package/design-system/components/Switch/README.md +44 -24
- package/design-system/components/Switch/preview.html +23 -8
- package/design-system/components/Switch/style.css +31 -0
- package/design-system/components/Table/README.md +129 -58
- package/design-system/components/Table/preview.html +106 -72
- package/design-system/components/Table/style.css +104 -0
- package/design-system/components/Tabs/README.md +71 -46
- package/design-system/components/Tabs/preview.html +56 -24
- package/design-system/components/Tabs/style.css +44 -0
- package/design-system/components/Testimonial/README.md +48 -32
- package/design-system/components/Testimonial/preview.html +14 -14
- package/design-system/components/Testimonial/style.css +12 -0
- package/design-system/components/TextArea/README.md +27 -11
- package/design-system/components/TextArea/preview.html +8 -2
- package/design-system/components/TextArea/style.css +12 -0
- package/design-system/components/ThemeSwitch/README.md +116 -0
- package/design-system/components/ThemeSwitch/preview.html +57 -0
- package/design-system/components/ThemeSwitch/style.css +6 -0
- package/design-system/components/TickRow/README.md +52 -36
- package/design-system/components/TickRow/preview.html +64 -41
- package/design-system/components/TickRow/style.css +23 -0
- package/design-system/components/TickStrip/README.md +95 -0
- package/design-system/components/TickStrip/preview.html +88 -0
- package/design-system/components/TickStrip/style.css +42 -0
- package/design-system/components/Tile/README.md +79 -18
- package/design-system/components/Tile/preview.html +91 -40
- package/design-system/components/Tile/style.css +36 -0
- package/design-system/components/Timeline/README.md +68 -48
- package/design-system/components/Timeline/preview.html +27 -15
- package/design-system/components/Timeline/style.css +25 -0
- package/design-system/components/Toast/README.md +70 -38
- package/design-system/components/Toast/preview.html +43 -8
- package/design-system/components/Toast/style.css +25 -0
- package/design-system/components/Tooltip/README.md +93 -35
- package/design-system/components/Tooltip/preview.html +69 -15
- package/design-system/components/Tooltip/style.css +78 -0
- package/design-system/components/TopBar/README.md +67 -53
- package/design-system/components/TopBar/preview.html +14 -27
- package/design-system/components/TopBar/style.css +35 -0
- package/design-system/components/TopicRail/README.md +108 -0
- package/design-system/components/TopicRail/preview.html +58 -0
- package/design-system/components/TopicRail/style.css +48 -0
- package/design-system/components/TrendBars/README.md +52 -14
- package/design-system/components/TrendBars/preview.html +65 -32
- package/design-system/components/TrendBars/style.css +25 -0
- package/design-system/components/TrendLine/README.md +164 -0
- package/design-system/components/TrendLine/preview.html +373 -0
- package/design-system/components/TrendLine/style.css +155 -0
- package/design-system/components/Well/README.md +64 -22
- package/design-system/components/Well/preview.html +14 -14
- package/design-system/components/Well/style.css +22 -0
- package/design-system/components/_house.css +324 -0
- package/design-system/components/bundle.css +4086 -744
- package/design-system/tokens.json +1310 -124
- package/dist/fello-core.css +2 -2
- package/dist/fello.css +6 -1369
- package/dist/fello.scoped.css +7 -0
- package/dist/index.d.ts +109 -10
- package/dist/index.js +781 -96
- package/dist/palettes.d.ts +464 -0
- package/dist/palettes.js +497 -0
- package/dist/scoped.d.ts +3 -0
- package/dist/scoped.js +16 -0
- package/dist/toast.d.ts +14 -0
- package/dist/toast.js +33 -29
- package/dist/tokens.d.ts +1686 -62
- package/dist/tokens.js +1668 -61
- package/package.json +20 -4
- package/scripts/check-palette.mjs +295 -0
- package/scripts/token-model.mjs +552 -0
- package/tokens/_dark.css +173 -0
- package/tokens/_palettes.css +46 -0
- package/tokens/_product.css +266 -53
- package/tokens/_source.hubl.css +39 -19
- package/tokens/design-tokens.json +319 -0
- package/tokens/palettes.ts +504 -0
- package/tokens/shadcn-compat.css +19 -19
- package/tokens/shadcn-compat.hsl.css +76 -0
- package/tokens/tokens.css +1287 -62
- package/tokens/tokens.md +587 -61
- package/tokens/tokens.root.css +1293 -62
- package/tokens/tokens.ts +1677 -64
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/* ---- AppShell: a product's frame, Seth's North (D1). The 232 px nav on the frame's wash, a glass stage 13 down and 12 in
|
|
2
|
+
from the window's edge, its 56 px bar and a body that owns the scroll. It fills its parent's height: give that parent
|
|
3
|
+
the window's (100dvh). It measures itself: under 60rem of its own width it is the phone form (no nav, a menu button,
|
|
4
|
+
the stage edge to edge). The gutter is Seth's 32 (D4, --ws-gutter). ---- */
|
|
5
|
+
.ws-app-shell { container: ws-app-shell / inline-size; display: flex; height: 100%; min-height: 0; background: var(--ws-frame); }
|
|
6
|
+
|
|
7
|
+
/* The nav: 232 open, 64 folded, on the stage's 13 / 12 insets. Its head sits on the bar's line (past the stage's 1 px edge, the 56 height), the
|
|
8
|
+
groups scroll between the head and the person, who never scrolls away (K11). */
|
|
9
|
+
.ws-app-shell__nav { display: flex; flex-direction: column; min-height: 0; padding-block: var(--ws-line) var(--space-xs); overflow-x: hidden; overflow-y: auto; scrollbar-width: none; }
|
|
10
|
+
.ws-app-shell > .ws-app-shell__nav { width: var(--ws-sidebar-width); flex-shrink: 0; margin-block: var(--ws-stage-inset-top) var(--ws-stage-inset); }
|
|
11
|
+
/* Head and person sit on the groups' 12 / 8 sides and take a row's 10 inside them (North: the nav's 12, the head's 10),
|
|
12
|
+
so the name and the avatar start on the row icons' edge, 22 in, from two steps on the scale. */
|
|
13
|
+
.ws-app-shell__head { display: flex; align-items: center; gap: var(--space-xs); height: var(--ws-header-h); padding-inline: var(--space-sm) var(--space-xs); }
|
|
14
|
+
.ws-app-shell__wordmark { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--font-size-lg); line-height: var(--space-lg); font-weight: 600; color: var(--color-text-heading); }
|
|
15
|
+
.ws-app-shell__head > .ws-app-shell__wordmark { flex: 1; padding-inline-start: var(--space-xs-plus); }
|
|
16
|
+
.ws-app-shell__groups { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--ws-ink) 18%, transparent) transparent; }
|
|
17
|
+
/* A group: rows at x 12, 212 wide; the 1 px rule between groups runs the nav's full width. Markup from 0.4 (eyebrows and
|
|
18
|
+
rows straight in the nav) keeps the same edges. */
|
|
19
|
+
.ws-app-shell__group { padding: var(--space-xxs) var(--space-xs) var(--space-xxs) var(--space-sm); }
|
|
20
|
+
.ws-app-shell__group + .ws-app-shell__group { border-top: var(--ws-line) solid var(--ws-edge-card); }
|
|
21
|
+
.ws-app-shell__nav > :is(.ws-app-shell__rows, .ws-app-shell__eyebrow, .ws-app-shell__wordmark) { margin-inline: var(--space-sm) var(--space-xs); }
|
|
22
|
+
.ws-app-shell__nav > .ws-app-shell__wordmark { flex-shrink: 0; display: flex; align-items: center; min-height: var(--ws-header-h); padding-inline: var(--space-xs-plus); }
|
|
23
|
+
/* A group's name: Seth's sentence-case head, 11 / 16 medium (his 10 px, at the kit's 11 px floor), on the icons' edge. */
|
|
24
|
+
.ws-app-shell__eyebrow { display: block; min-height: var(--ws-control-h-sm); padding: var(--space-sm) var(--space-xs-plus) 0; font-size: var(--ws-text-micro); line-height: var(--space-md); font-weight: 500; letter-spacing: normal; text-transform: none; color: var(--color-text-muted); }
|
|
25
|
+
.ws-app-shell__rows { display: flex; flex-direction: column; gap: var(--space-3xs); }
|
|
26
|
+
|
|
27
|
+
/* A row: 28 tall (40 on touch, K10), 2 apart, 13 px medium words, a 20 px icon box, 6 px corners. Hover is Seth's navy
|
|
28
|
+
wash; the current page is a white pill with his coral, the word at its AA step (D2). */
|
|
29
|
+
.ws-app-shell__row { position: relative; display: flex; align-items: center; gap: var(--space-xxs); width: 100%; height: var(--ws-control-h-sm); padding: 0 var(--space-xs-plus); border: 0; border-radius: var(--ws-radius-row); background: none; text-align: left; text-decoration: none; font-size: var(--ws-text-control); line-height: var(--ws-leading-control); font-weight: 500; color: var(--color-text-body); cursor: pointer; }
|
|
30
|
+
.ws-app-shell__row > svg { flex-shrink: 0; width: var(--ws-icon-lg); height: var(--ws-icon-lg); padding: var(--space-3xs); color: var(--ws-mark-muted); }
|
|
31
|
+
.ws-app-shell__row:focus-visible { outline-offset: calc(var(--ws-focus-width) * -1); }
|
|
32
|
+
@media (hover: hover) { .ws-app-shell__row:hover { background: var(--ws-hover); } }
|
|
33
|
+
.ws-app-shell__row:active { background: var(--ws-hover-strong); }
|
|
34
|
+
.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"]) { border-radius: var(--ws-radius-control); background: var(--color-surface); box-shadow: var(--ws-float-rim), 0 var(--ws-line) var(--ws-ring) color-mix(in srgb, var(--ws-shade) 6%, transparent), 0 0 0 var(--ws-hairline) color-mix(in srgb, var(--ws-shade) 4%, transparent); color: var(--color-text-brand-strong); }
|
|
35
|
+
.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"]) > svg { color: var(--ws-chart-under-half); }
|
|
36
|
+
.ws-app-shell__name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
37
|
+
/* A row's count: Seth's navy pill, 18 tall, its figure on the act fill's word colour. */
|
|
38
|
+
.ws-app-shell__count { flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; min-width: calc(var(--space-md) + var(--space-3xs)); height: calc(var(--space-md) + var(--space-3xs)); padding: 0 var(--space-xxs); border-radius: var(--radius-full); background: var(--color-text-heading); color: var(--ws-on-act); font-size: var(--ws-text-micro); line-height: 1; font-weight: 600; font-variant-numeric: tabular-nums; }
|
|
39
|
+
|
|
40
|
+
/* The person, pinned at the foot under a full-width rule. */
|
|
41
|
+
.ws-app-shell__foot { flex-shrink: 0; display: flex; align-items: center; gap: var(--space-xs-plus); margin-top: auto; padding: var(--space-sm) var(--space-xs) var(--space-xxs-plus) var(--space-sm); border-top: var(--ws-line) solid var(--ws-edge-card); }
|
|
42
|
+
.ws-app-shell__foot > :first-child { margin-inline-start: var(--space-xs-plus); }
|
|
43
|
+
.ws-app-shell__foot > :last-child { margin-inline-end: var(--space-xs-plus); }
|
|
44
|
+
/* 0.4 markup (wordmark, rows and person straight in the nav, no __groups): the nav itself scrolls, so the wordmark and
|
|
45
|
+
the person stick to its ends (K11), the nav's full width, on the frame's wash fixed to the window as the frame is, so
|
|
46
|
+
no seam shows. The nav's own 1 / 8 ends move into them, so no row peeks past either. */
|
|
47
|
+
.ws-app-shell__nav:not(:has(> .ws-app-shell__groups)) { padding-block: 0; }
|
|
48
|
+
.ws-app-shell__nav:not(:has(> .ws-app-shell__groups)) > :is(.ws-app-shell__wordmark, .ws-app-shell__foot) { position: sticky; z-index: 1; background: var(--ws-frame); background-attachment: fixed; }
|
|
49
|
+
.ws-app-shell__nav:not(:has(> .ws-app-shell__groups)) > .ws-app-shell__wordmark { top: 0; min-height: calc(var(--ws-header-h) + var(--ws-line)); margin-inline: 0; padding: var(--ws-line) var(--space-xs) 0 calc(var(--space-sm) + var(--space-xs-plus)); }
|
|
50
|
+
.ws-app-shell__nav:not(:has(> .ws-app-shell__groups)) > .ws-app-shell__foot { bottom: 0; padding-bottom: var(--space-sm-plus); }
|
|
51
|
+
.ws-app-shell__foot-words { display: flex; flex-direction: column; min-width: 0; }
|
|
52
|
+
.ws-app-shell__foot-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--ws-text-control); line-height: var(--space-md); font-weight: 500; color: var(--color-text-heading); }
|
|
53
|
+
|
|
54
|
+
/* Folded: the same nav as a 64 px rail of 40 px rows, names faded (still read aloud), counts gone, no group names or
|
|
55
|
+
rules. Each row carries its name and count in title. */
|
|
56
|
+
.ws-app-shell--folded > .ws-app-shell__nav { width: var(--ws-rail-width); }
|
|
57
|
+
.ws-app-shell--folded .ws-app-shell__head { justify-content: flex-end; gap: 0; padding-inline: 0 var(--space-md); }
|
|
58
|
+
@media (pointer: coarse) { .ws-app-shell--folded .ws-app-shell__head { padding-inline-end: var(--space-sm); } }
|
|
59
|
+
.ws-app-shell--folded :is(.ws-app-shell__head > .ws-app-shell__wordmark, .ws-app-shell__foot-words) { flex: 0 0 0; padding: 0; opacity: 0; }
|
|
60
|
+
.ws-app-shell--folded :is(.ws-app-shell__group, .ws-app-shell__nav > .ws-app-shell__rows) { padding-inline: var(--space-sm); }
|
|
61
|
+
.ws-app-shell--folded .ws-app-shell__nav > .ws-app-shell__rows { margin-inline: 0; }
|
|
62
|
+
.ws-app-shell--folded .ws-app-shell__row { overflow: hidden; }
|
|
63
|
+
.ws-app-shell--folded .ws-app-shell__name { opacity: 0; }
|
|
64
|
+
/* The count leaves the rail; the row's title carries it ("Pipeline, 7 waiting"), read as its description. */
|
|
65
|
+
.ws-app-shell--folded .ws-app-shell__count { display: none; }
|
|
66
|
+
.ws-app-shell--folded .ws-app-shell__eyebrow { display: none; }
|
|
67
|
+
.ws-app-shell--folded .ws-app-shell__group + .ws-app-shell__group { border-top-color: transparent; }
|
|
68
|
+
/* The person takes the rail's own 64 at once, so the avatar sits centred on the rail from the fold's first frame. */
|
|
69
|
+
.ws-app-shell--folded .ws-app-shell__foot { width: var(--ws-rail-width); justify-content: center; gap: 0; padding-inline: 0; border-top-color: transparent; }
|
|
70
|
+
.ws-app-shell--folded .ws-app-shell__foot > * { margin-inline: 0; }
|
|
71
|
+
/* A rail drawn on its own (0.4 markup): the same 64 px rail of 40 x 36 marks. */
|
|
72
|
+
.ws-app-shell__rail { width: var(--ws-rail-width); flex-shrink: 0; display: flex; flex-direction: column; align-items: center; gap: var(--space-3xs); margin-block: var(--ws-stage-inset-top) var(--ws-stage-inset); padding-block: var(--ws-line) var(--space-xs); }
|
|
73
|
+
.ws-app-shell__mark { display: flex; align-items: center; justify-content: center; width: var(--space-2xl); height: var(--ws-control-h-md); border-radius: var(--ws-radius-row); color: var(--color-text-body); }
|
|
74
|
+
.ws-app-shell__mark--wordmark { height: var(--ws-header-h); font-size: var(--font-size-lg); font-weight: 600; color: var(--color-text-heading); }
|
|
75
|
+
.ws-app-shell__mark--current { border-radius: var(--ws-radius-control); background: var(--color-surface); box-shadow: var(--ws-float-rim), 0 var(--ws-line) var(--ws-ring) color-mix(in srgb, var(--ws-shade) 6%, transparent), 0 0 0 var(--ws-hairline) color-mix(in srgb, var(--ws-shade) 4%, transparent); color: var(--ws-chart-under-half); }
|
|
76
|
+
.ws-app-shell__mark svg { width: var(--ws-icon); height: var(--ws-icon); }
|
|
77
|
+
|
|
78
|
+
/* The stage: Seth's glass, 16 px corners, his 1 px edge; --wash is the Fia frames' cream stage. The blur sits on a layer
|
|
79
|
+
under the content (::before), not on the stage: an element with a backdrop-filter is the box of every position: fixed
|
|
80
|
+
thing inside it, so a toaster, a phone's full-screen panel or a full-screen card would fill only the stage and be cut
|
|
81
|
+
to it. Through the layer they reach the window. */
|
|
82
|
+
.ws-app-shell__stage { position: relative; isolation: isolate; flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; margin: var(--ws-stage-inset-top) var(--ws-stage-inset) var(--ws-stage-inset) 0; border: var(--ws-line) solid var(--ws-edge-stage); border-radius: var(--ws-radius-panel); background: var(--ws-glass); overflow: clip; }
|
|
83
|
+
.ws-app-shell__stage::before { content: ""; position: absolute; inset: 0; z-index: -1; -webkit-backdrop-filter: blur(var(--ws-glass-blur)); backdrop-filter: blur(var(--ws-glass-blur)); pointer-events: none; }
|
|
84
|
+
.ws-app-shell--wash .ws-app-shell__stage { background: var(--ws-stage-wash); }
|
|
85
|
+
.ws-app-shell--wash .ws-app-shell__stage::before { content: none; }
|
|
86
|
+
/* The bar: the 56 px line on the content's 32 edges, flat at rest; its 0.5 px edge shows once the body scrolls under it
|
|
87
|
+
(set --scrolled from the body's scroll). It holds the page's name (or a Breadcrumb, or a PageBar) and its acts; 8
|
|
88
|
+
above and below, so a bar that wraps (a PageBar on a phone) never touches the stage's edge. */
|
|
89
|
+
.ws-app-shell__bar { flex-shrink: 0; display: flex; align-items: center; gap: var(--space-md); min-height: var(--ws-header-h); padding: var(--space-xs) var(--ws-gutter); }
|
|
90
|
+
.ws-app-shell__bar--scrolled { box-shadow: inset 0 calc(var(--ws-hairline) * -1) 0 var(--ws-edge-card); }
|
|
91
|
+
.ws-app-shell__title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--font-size-md); line-height: var(--ws-leading-section); font-weight: 600; letter-spacing: var(--ws-tracking-title); color: var(--color-text-heading); }
|
|
92
|
+
.ws-app-shell__acts { display: flex; flex-shrink: 0; align-items: center; gap: var(--space-xs); margin-inline-start: auto; }
|
|
93
|
+
.ws-app-shell__search { display: flex; flex-shrink: 1; align-items: center; gap: var(--space-xxs); width: calc(var(--space-9xl) + var(--space-md) + var(--space-xxs)); min-width: var(--space-8xl); height: var(--ws-control-h); padding: 0 var(--space-xs); border-radius: var(--ws-radius-control); background: var(--ws-field); box-shadow: inset 0 0 0 var(--ws-hairline) var(--ws-edge-card); color: var(--color-text-muted); font-size: var(--ws-text-meta); white-space: nowrap; overflow: hidden; cursor: text; }
|
|
94
|
+
.ws-app-shell__search svg { width: var(--ws-icon-sm); height: var(--ws-icon-sm); flex-shrink: 0; }
|
|
95
|
+
/* The search is a real field: a <label> round an <input type="search">; the ring goes on the box, not the bare input. */
|
|
96
|
+
.ws-app-shell__search > input { flex: 1; min-width: 0; height: 100%; padding: 0; border: 0; background: none; -webkit-appearance: none; appearance: none; font: inherit; color: var(--ws-field-ink); }
|
|
97
|
+
.ws-app-shell__search > input::placeholder { color: var(--color-text-muted); opacity: 1; }
|
|
98
|
+
.ws-app-shell__search > input:focus-visible { outline: none; }
|
|
99
|
+
.ws-app-shell__search:focus-within { outline: var(--ws-focus-width) solid var(--ws-focus-color); outline-offset: var(--ws-focus-offset); }
|
|
100
|
+
@media (pointer: coarse) { .ws-app-shell__search { height: var(--ws-touch); font-size: var(--ws-text-value); } } /* 16 px words: a phone does not zoom into the field on focus */
|
|
101
|
+
.ws-app-shell .ws-app-shell__menu { display: none; } /* two classes: IconButton's display sits later in the bundle */
|
|
102
|
+
/* The body is the page: it scrolls (and never chains into the window), sizes what is inside it (a container, as .ws-page
|
|
103
|
+
is) and caps its content at the kit's content width on one start edge (K12). No .ws-page inside it. */
|
|
104
|
+
.ws-app-shell__body { flex: 1; min-width: 0; min-height: 0; container-type: inline-size; display: flex; flex-direction: column; gap: var(--ws-gap-component); padding: var(--space-md) var(--ws-gutter) var(--space-2xl); overflow: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--ws-ink) 18%, transparent) transparent; }
|
|
105
|
+
.ws-app-shell__stage > .ws-app-shell__body:first-child { padding-top: var(--ws-gutter); }
|
|
106
|
+
.ws-app-shell :is(.ws-app-shell__body, .ws-app-shell__groups):focus-visible { outline-offset: calc(var(--ws-focus-width) * -1); }
|
|
107
|
+
/* The content's cap is the board's two columns, as on .ws-page (Seth's North: 1119), so a section, its tiles and a
|
|
108
|
+
board all end on one edge. */
|
|
109
|
+
.ws-app-shell__body > * { max-width: calc(var(--ws-chart-card-w) * 2 + var(--ws-gap-component)); }
|
|
110
|
+
/* --wide: a Fia app page (Grow, Fixes, Inbox, Tally) runs the body's full width, Seth's 1350 in a 1416 stage. */
|
|
111
|
+
.ws-app-shell__body--wide > * { max-width: none; }
|
|
112
|
+
/* A filled page (a --fill Split) ends 24 above the stage's foot, Seth's drawer inset (301:2011), not the page's 40. */
|
|
113
|
+
.ws-app-shell__body:has(> .ws-split--fill) { padding-bottom: max(var(--ws-gap-group), env(safe-area-inset-bottom)); }
|
|
114
|
+
|
|
115
|
+
/* The phone form, under 60rem of the shell's own width (North drops the nav at 960): the nav leaves for a drawer opened
|
|
116
|
+
from the bar's menu button (the same nav markup, at the drawer's width), the stage goes edge to edge, 16 px sides. */
|
|
117
|
+
@container ws-app-shell (max-width: 60rem) {
|
|
118
|
+
.ws-app-shell > :is(.ws-app-shell__nav, .ws-app-shell__rail) { display: none; }
|
|
119
|
+
.ws-app-shell__stage { margin: 0; border: 0; border-radius: 0; }
|
|
120
|
+
.ws-app-shell .ws-app-shell__menu { display: inline-flex; }
|
|
121
|
+
.ws-app-shell__search { display: none; }
|
|
122
|
+
.ws-app-shell__bar, .ws-app-shell__body { padding-inline: max(var(--space-md), env(safe-area-inset-left)) max(var(--space-md), env(safe-area-inset-right)); }
|
|
123
|
+
.ws-app-shell .ws-app-shell__bar { padding-top: max(var(--space-xs), env(safe-area-inset-top)); }
|
|
124
|
+
.ws-app-shell__body { padding-bottom: max(var(--space-2xl), env(safe-area-inset-bottom)); }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* The current pill slides (North "Sidebar active row slide", health.navSlide, springs.nav .4 / .1; north-motion 3): a
|
|
128
|
+
white pill slides to the new page's row instead of jumping. The current row is the anchor (CSS anchor positioning)
|
|
129
|
+
and the rows' scroller draws the travelling pill (its ::before: __groups, or a 0.4 nav that holds its rows), under
|
|
130
|
+
the rows, its four edges easing on motion's own spring(.4, .1) written out as linear() (550 ms). At rest the current
|
|
131
|
+
row paints its own pill, so a resting nav is the 0.4 nav pixel for pixel; while the rows are pointed at or hold focus
|
|
132
|
+
the travelling pill takes its place (the same box, the same paint, at once), so a page chosen then slides to. The
|
|
133
|
+
fold (its button is outside the rows) and a resize carry the pill with its row at once. No new markup. Without
|
|
134
|
+
anchor positioning, or under reduced motion, the pill moves at once. The spring is the global --ws-spring-nav over --ws-spring-nav-duration. The 0.4 rail's
|
|
135
|
+
marks stay put. */
|
|
136
|
+
@supports (anchor-name: --a) {
|
|
137
|
+
:is(.ws-app-shell__groups, .ws-app-shell__nav:not(:has(> .ws-app-shell__groups))) { position: relative; anchor-scope: --ws-app-shell-current; }
|
|
138
|
+
:is(.ws-app-shell__groups, .ws-app-shell__nav:not(:has(> .ws-app-shell__groups)))::before { content: ""; position: absolute; position-anchor: --ws-app-shell-current; inset: anchor(top) anchor(right) anchor(bottom) anchor(left); border-radius: var(--ws-radius-control); background: var(--color-surface); box-shadow: var(--ws-float-rim), 0 var(--ws-line) var(--ws-ring) color-mix(in srgb, var(--ws-shade) 6%, transparent), 0 0 0 var(--ws-hairline) color-mix(in srgb, var(--ws-shade) 4%, transparent); opacity: 0; pointer-events: none; }
|
|
139
|
+
.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"]) { anchor-name: --ws-app-shell-current; }
|
|
140
|
+
.ws-app-shell__eyebrow { position: relative; } /* a group's name stays over the pill as it passes, as the rows do */
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* Motion (North): the fold trades width on the shift curve, names fade, washes and the bar's edge ease in. */
|
|
144
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
145
|
+
.ws-app-shell > .ws-app-shell__nav { transition: width var(--ws-duration-shift) var(--ws-ease-shift); }
|
|
146
|
+
.ws-app-shell :is(.ws-app-shell__wordmark, .ws-app-shell__name, .ws-app-shell__foot-words) { transition: opacity var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
147
|
+
.ws-app-shell__row { transition: background-color var(--ws-duration-hover) var(--ws-ease-soft), color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
148
|
+
.ws-app-shell__bar { transition: box-shadow var(--ws-duration-settle) var(--ws-ease-soft); }
|
|
149
|
+
@supports (anchor-name: --a) {
|
|
150
|
+
:is(.ws-app-shell__groups, .ws-app-shell__nav:not(:has(> .ws-app-shell__groups))):is(:hover, :focus-within)::before { opacity: 1; transition: inset var(--ws-spring-nav-duration) var(--ws-spring-nav); }
|
|
151
|
+
:is(.ws-app-shell__groups, .ws-app-shell__nav:not(:has(> .ws-app-shell__groups))):is(:hover, :focus-within) .ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"]) { background: none; box-shadow: none; }
|
|
152
|
+
.ws-app-shell__row:is(.ws-app-shell__row--current, [aria-current="page"]) { transition: color var(--ws-duration-hover) var(--ws-ease-soft); }
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -17,14 +17,17 @@ never coloured, never an app's face.
|
|
|
17
17
|
|
|
18
18
|
- Shape: a disc, `radius-full`, `overflow: hidden` so a photo never spills past its edge.
|
|
19
19
|
- Ground: `--ws-surface-sunk`, with navy initials (`--color-text-heading`) semibold, sized to the disc (12 to
|
|
20
|
-
|
|
21
|
-
- A photo
|
|
22
|
-
|
|
20
|
+
24 px across the sizes below: 12, 14, 14, 16, 24).
|
|
21
|
+
- A photo: an `<img alt="">` inside the disc, after the initials, covering it (`object-fit: cover`). The initials stay
|
|
22
|
+
under it, so the disc is never empty: while it loads, and if it never does. With `data-state="loading"` (the React
|
|
23
|
+
component sets it, or a page script until `load`) it waits hidden, then fades in over 200 ms (`--ws-duration-gentle`
|
|
24
|
+
on `--ws-ease-soft`); without it the photo simply shows when the browser has it.
|
|
23
25
|
- `ring`: an outer hairline (`--ws-shadow-hairline`) so the disc reads on a cream card.
|
|
24
|
-
- `dim`:
|
|
26
|
+
- `dim`: for someone invited who has not joined yet: the initials in the muted word (`--color-text-muted`, AA on every
|
|
27
|
+
ground), a photo at 60% opacity. The disc itself never fades, so its initials never drop under AA.
|
|
25
28
|
- In fello.css: `.ws-avatar` is the 32 px disc with the ring (it mostly sits on a cream card); `--plain` drops the
|
|
26
29
|
ring, `--dim` dims it, `--sm`, `--deck`, `--panel` and `--xl` size it. A stack is `.ws-avatar-stack` with
|
|
27
|
-
`.ws-avatar-stack__more` on the "+N" disc.
|
|
30
|
+
`.ws-avatar-stack__more` on the "+N" disc (tabular figures, so "+9" and "+12" sit alike).
|
|
28
31
|
|
|
29
32
|
## Variants and sizes
|
|
30
33
|
|
|
@@ -44,16 +47,18 @@ steps `sm` 28, `md` 32, `deck` 48, `panel` 56, `xl` 64. This README and preview
|
|
|
44
47
|
|
|
45
48
|
- **Default**: initials on the sunk ground.
|
|
46
49
|
- **Photo loaded**: the photo fills the disc, cropped to cover.
|
|
47
|
-
- **Loading**: initials show from the first frame;
|
|
48
|
-
browser already had it cached).
|
|
50
|
+
- **Loading**: initials show from the first frame; a photo marked `data-state="loading"` crossfades in once the mark is
|
|
51
|
+
dropped on `load` (or at once if the browser already had it cached).
|
|
52
|
+
- **Photo failed**: the initials stay; nothing breaks, nothing shows a broken image (drop the `<img>` on `error`).
|
|
49
53
|
- **Ring**: the hairline edge, for a disc sitting directly on a cream card with nothing else to separate them.
|
|
50
|
-
- **Dim**: 60%
|
|
54
|
+
- **Dim**: muted initials (a photo at 60%), for an invited person who has not joined.
|
|
51
55
|
- **Stack overflow**: a disc in the same ground reading "+N" in place of a fifth face.
|
|
52
56
|
|
|
53
57
|
## Behaviour
|
|
54
58
|
|
|
55
59
|
- Decorative: `aria-hidden`, since whoever uses it names the person in words beside it (a row's title, a card's
|
|
56
|
-
label) or in its own accessible label.
|
|
60
|
+
label) or in its own accessible label. A stack that stands alone takes `role="img"` and a label naming the people
|
|
61
|
+
("Priya Nair, Dana Sato and 12 more").
|
|
57
62
|
- No keyboard or pointer behaviour of its own: it carries no click.
|
|
58
63
|
- No motion but the photo's own crossfade; nothing moves with reduced motion beyond that opacity change settling
|
|
59
64
|
at once instead of easing.
|
|
@@ -70,7 +75,7 @@ steps `sm` 28, `md` 32, `deck` 48, `panel` 56, `xl` 64. This README and preview
|
|
|
70
75
|
| `name` | `string` | Used for the initials and the alt text context |
|
|
71
76
|
| `photo` | `string \| null` | A URL to their picture, or `null` for initials only |
|
|
72
77
|
| `size` | `'sm' \| 'md' \| 'deck' \| 'panel' \| 'xl'` | The disc's size |
|
|
73
|
-
| `dim` | `boolean` | 60%
|
|
78
|
+
| `dim` | `boolean` | Muted initials (a photo at 60%), for someone invited who has not joined |
|
|
74
79
|
| `ring` | `boolean` | The hairline edge for a cream card |
|
|
75
80
|
|
|
76
81
|
`AvatarStack` takes `people: { name, photo }[]` and shows the first four, overlapping by 4 px, with "+N" for the
|
|
@@ -90,3 +95,25 @@ rest.
|
|
|
90
95
|
|
|
91
96
|
**Derived from**: the system rules for people as neutral navy-initial discs, the sizing scale and the hairline
|
|
92
97
|
ring for a cream card. **Precedent**: the first product built on the system.
|
|
98
|
+
|
|
99
|
+
## Markup
|
|
100
|
+
|
|
101
|
+
```html
|
|
102
|
+
<span class="ws-avatar ws-avatar--sm" aria-hidden="true">PN</span>
|
|
103
|
+
|
|
104
|
+
<span class="ws-avatar ws-avatar--plain" aria-hidden="true">DS<img alt="" src="/people/dana.jpg"></span>
|
|
105
|
+
|
|
106
|
+
<div class="ws-avatar-stack" role="img" aria-label="Priya Nair, Dana Sato, Sam Chen, Nina Ross and 12 more">
|
|
107
|
+
<span class="ws-avatar ws-avatar--sm">PN</span>
|
|
108
|
+
<span class="ws-avatar ws-avatar--sm">DS</span>
|
|
109
|
+
<span class="ws-avatar ws-avatar--sm">SC</span>
|
|
110
|
+
<span class="ws-avatar ws-avatar--sm">NR</span>
|
|
111
|
+
<span class="ws-avatar ws-avatar--sm ws-avatar-stack__more">+12</span>
|
|
112
|
+
</div>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Styles
|
|
116
|
+
|
|
117
|
+
The disc, its sizes and the stack are house styles (`design-system/components/_house.css`, "Rows and lists"); the photo
|
|
118
|
+
layer is `design-system/components/Avatar/style.css`. Both are in `bundle.css` and `fello.css`. Dark comes from the
|
|
119
|
+
tokens: the sunk ground, the navy initials (`--color-text-heading`), the stack's rings in `--color-bg-page`.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- @dsCard group="People" height=
|
|
1
|
+
<!-- @dsCard group="People" height=420 -->
|
|
2
2
|
<!doctype html>
|
|
3
3
|
<html>
|
|
4
4
|
<head>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
<div class="ws-stack" style="align-items: flex-start;">
|
|
14
14
|
<span class="ws-muted">Sizes: sm 28, md 32, deck 48, panel 56, xl 64 (initials until a photo loads)</span>
|
|
15
|
-
<div class="ws-cluster ws-cluster--group">
|
|
15
|
+
<div class="ws-cluster ws-cluster--group" style="align-items: flex-end;">
|
|
16
16
|
<div class="ws-stack ws-stack--tight" style="align-items: center;"><span class="ws-avatar ws-avatar--plain ws-avatar--sm" aria-hidden="true">PN</span><span class="ws-muted">sm</span></div>
|
|
17
17
|
<div class="ws-stack ws-stack--tight" style="align-items: center;"><span class="ws-avatar ws-avatar--plain" aria-hidden="true">PN</span><span class="ws-muted">md</span></div>
|
|
18
18
|
<div class="ws-stack ws-stack--tight" style="align-items: center;"><span class="ws-avatar ws-avatar--plain ws-avatar--deck" aria-hidden="true">PN</span><span class="ws-muted">deck</span></div>
|
|
@@ -21,6 +21,15 @@
|
|
|
21
21
|
</div>
|
|
22
22
|
</div>
|
|
23
23
|
|
|
24
|
+
<div class="ws-stack" style="align-items: flex-start;">
|
|
25
|
+
<span class="ws-muted">Photo: it covers the disc once loaded, the initials stay under it (the stand-in picture here is a drawing, not a person)</span>
|
|
26
|
+
<div class="ws-cluster ws-cluster--group" style="align-items: flex-end;">
|
|
27
|
+
<div class="ws-stack ws-stack--tight" style="align-items: center;"><span class="ws-avatar ws-avatar--plain" aria-hidden="true">DS<img alt="" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23cfc8c0'/%3E%3Ccircle cx='32' cy='26' r='12' fill='%23a39a90'/%3E%3Cpath d='M10 64c2-14 12-22 22-22s20 8 22 22z' fill='%23a39a90'/%3E%3C/svg%3E"></span><span class="ws-muted">loaded</span></div>
|
|
28
|
+
<div class="ws-stack ws-stack--tight" style="align-items: center;"><span class="ws-avatar ws-avatar--plain" aria-hidden="true">DS<img alt="" data-state="loading" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23cfc8c0'/%3E%3C/svg%3E"></span><span class="ws-muted">loading</span></div>
|
|
29
|
+
<div class="ws-stack ws-stack--tight" style="align-items: center;"><span class="ws-avatar ws-avatar--plain ws-avatar--panel" aria-hidden="true">DS<img alt="" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' fill='%23cfc8c0'/%3E%3Ccircle cx='32' cy='26' r='12' fill='%23a39a90'/%3E%3Cpath d='M10 64c2-14 12-22 22-22s20 8 22 22z' fill='%23a39a90'/%3E%3C/svg%3E"></span><span class="ws-muted">panel</span></div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
24
33
|
<div class="ws-stack" style="align-items: flex-start;">
|
|
25
34
|
<span class="ws-muted">Ring (on a cream card) and dim (invited, not yet joined)</span>
|
|
26
35
|
<div class="ws-cluster ws-cluster--group">
|
|
@@ -31,12 +40,12 @@
|
|
|
31
40
|
|
|
32
41
|
<div class="ws-stack" style="align-items: flex-start;">
|
|
33
42
|
<span class="ws-muted">AvatarStack: four faces overlapping by 4 px on cream rings, the rest as "+N"</span>
|
|
34
|
-
<div class="ws-avatar-stack" aria-
|
|
43
|
+
<div class="ws-avatar-stack" role="img" aria-label="Priya Nair, Dana Sato, Sam Chen, Nina Ross and 12 more">
|
|
35
44
|
<span class="ws-avatar ws-avatar--sm">PN</span>
|
|
36
45
|
<span class="ws-avatar ws-avatar--sm">DS</span>
|
|
37
46
|
<span class="ws-avatar ws-avatar--sm">SC</span>
|
|
38
47
|
<span class="ws-avatar ws-avatar--sm">NR</span>
|
|
39
|
-
<span class="ws-avatar ws-avatar--sm ws-avatar-stack__more">+
|
|
48
|
+
<span class="ws-avatar ws-avatar--sm ws-avatar-stack__more">+12</span>
|
|
40
49
|
</div>
|
|
41
50
|
</div>
|
|
42
51
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/* ---- Avatar: the photo layer over the initials disc (the disc, its sizes and the stack are house styles). The photo
|
|
2
|
+
covers the disc once it loads; until then, and if it never does, the initials under it are the mark. ---- */
|
|
3
|
+
.ws-avatar { position: relative; }
|
|
4
|
+
.ws-avatar > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
|
|
5
|
+
/* A photo still loading (the React component sets data-state, or a page script on load) shows the initials; it fades in
|
|
6
|
+
over them once it has loaded. Without data-state the photo simply shows when the browser has it. */
|
|
7
|
+
.ws-avatar > img[data-state="loading"] { opacity: 0; }
|
|
8
|
+
@media (prefers-reduced-motion: no-preference) { .ws-avatar > img { transition: opacity var(--ws-duration-gentle) var(--ws-ease-soft); } }
|
|
9
|
+
/* Dim (invited, not yet joined): the disc stays whole and its initials take the muted word, AA on every ground (the
|
|
10
|
+
house fade of the whole disc left them at 3.7:1); a photo in it fades to 60 %. */
|
|
11
|
+
.ws-avatar--dim { opacity: 1; color: var(--color-text-muted); }
|
|
12
|
+
.ws-avatar--dim > img { opacity: 0.6; }
|
|
13
|
+
/* "+N" in a stack: the same disc, tabular figures, so +9 and +12 sit alike. */
|
|
14
|
+
.ws-avatar-stack__more { font-variant-numeric: tabular-nums; }
|
|
@@ -15,26 +15,23 @@ A small count or mark riding on the corner of a face, an icon or a nav row, sayi
|
|
|
15
15
|
|
|
16
16
|
## Anatomy
|
|
17
17
|
|
|
18
|
-
- Box:
|
|
19
|
-
|
|
20
|
-
- Words: 11 px semibold (`--ws-text-micro` at
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- Dot: 6 px, solid `--color-brand-salmon`, no words, for a face too small for a number.
|
|
24
|
-
- Position: overlapping the host's corner (a small negative offset), so it reads as riding on the host, not beside it.
|
|
18
|
+
- Box: an 18 px pill (`--space-md` + `--space-3xs`, `--radius-full`), at least as wide as it is tall, 6 px sides: North's
|
|
19
|
+
count. On a host's corner it is 16 px, 4 px sides, so it covers less of a face, with a 2 px ring of the page round it.
|
|
20
|
+
- Words: 11 px semibold (`--ws-text-micro` at 600), even numerals, so a count that changes does not jiggle.
|
|
21
|
+
- Dot: 6 px, solid salmon (`--ws-chart-under-half`), no words, for a face too small for a number.
|
|
22
|
+
- Position on a host: the top-right corner, 4 px out, riding on the host.
|
|
25
23
|
|
|
26
24
|
## Variants and sizes
|
|
27
25
|
|
|
28
26
|
| Tone | Fill | Words | Where |
|
|
29
27
|
| --- | --- | --- | --- |
|
|
28
|
+
| `navy` | `--color-text-heading` | `--ws-on-act` | a count in the nav or on a row (North's count) |
|
|
30
29
|
| `needs` | `--ws-tint-needs` | `--ws-word-needs` | a count that needs you (unread, overdue) |
|
|
31
30
|
| `neutral` | `--ws-surface-sunk` | `--color-text-heading` | a plain count with no urgency |
|
|
32
|
-
| dot | `--
|
|
31
|
+
| dot | `--ws-chart-under-half` | (none) | a face too small to hold a number |
|
|
33
32
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
component by being a different shape and role (an overlay mark, not an inline fact), not by adding a seventh `Chip`
|
|
37
|
-
tone.
|
|
33
|
+
The kit's marketing pill (`ws-kit-badge`, six tones) is shown captioned "kit, marketing only"; its words now take the AA
|
|
34
|
+
step on their own tint (brand, success and error were 2.3 to 3.6:1).
|
|
38
35
|
|
|
39
36
|
## States
|
|
40
37
|
|
|
@@ -44,15 +41,21 @@ tone.
|
|
|
44
41
|
- **On a nav row:** sits at the row's trailing edge.
|
|
45
42
|
- **On an icon button:** sits at the button's top-right corner.
|
|
46
43
|
- **The dot alone:** no number, no words, just the 6 px mark.
|
|
44
|
+
- **Default, navy:** the act's navy, cream numerals (in the nav).
|
|
45
|
+
- **Changed:** re-keyed with `ws-pop`, it pops in from 0.85 on the small-mark overshoot (`--ws-ease-pop`); still under
|
|
46
|
+
reduced motion.
|
|
47
47
|
- **Empty:** a badge with nothing to say is left out, never rendered at zero.
|
|
48
|
+
- **Dark:** every tone takes its dark values; the ring round a corner badge is the page's dark ground.
|
|
48
49
|
|
|
49
50
|
## Behaviour
|
|
50
51
|
|
|
51
52
|
- Keyboard / pointer: none of its own; a badge is a `<span>`, not a control. If the host is interactive (a nav row, an
|
|
52
53
|
icon button), the badge's count is read as part of that host's accessible name ("Needs you, 3"), never as a separate
|
|
53
|
-
focusable element
|
|
54
|
-
|
|
55
|
-
-
|
|
54
|
+
focusable element: inside a row it stays readable; on a face or an icon button it is `aria-hidden` and the host's
|
|
55
|
+
label carries the count ("Leads, 12 new"), never a count only the eye gets.
|
|
56
|
+
- Motion: a badge whose number changes is re-keyed with `ws-pop` and pops in (North's badge spring); one appearing with
|
|
57
|
+
its host rises with it (`ws-rise`).
|
|
58
|
+
- Reduced motion: still.
|
|
56
59
|
|
|
57
60
|
## Content
|
|
58
61
|
|
|
@@ -64,18 +67,18 @@ tone.
|
|
|
64
67
|
|
|
65
68
|
| Name | Type | Meaning |
|
|
66
69
|
| --- | --- | --- |
|
|
67
|
-
| `tone` | `'needs' \| 'neutral'` | the look, default `needs` |
|
|
70
|
+
| `tone` | `'navy' \| 'needs' \| 'neutral'` | the look, default `needs` |
|
|
68
71
|
| `count` | `number` | the number shown; omit together with `dot` to show nothing |
|
|
69
72
|
| `dot` | `boolean` | shows the plain salmon dot instead of a number |
|
|
70
73
|
| `max` | `number` | a count past this shows as `"${max}+"` |
|
|
71
74
|
|
|
72
75
|
## Rules
|
|
73
76
|
|
|
74
|
-
-
|
|
75
|
-
|
|
77
|
+
- Three tones only: the act's navy (a count in the nav, North's), and `Chip`'s "needs you" and neutral pairs, so a badge
|
|
78
|
+
never brings a colour the system does not already use.
|
|
76
79
|
- The dot is the one case with no words, used only where a number would crowd a small face, matching the system's
|
|
77
80
|
existing "a small salmon dot" needs-you mark used elsewhere on a sidebar face.
|
|
78
|
-
- Never a
|
|
81
|
+
- Never a fourth tone: a look that does not fit is a sign the screen needs a different component.
|
|
79
82
|
|
|
80
83
|
## Source
|
|
81
84
|
|
|
@@ -90,3 +93,22 @@ without overwhelming it), 11 px semibold instead of 12 px medium (the type floor
|
|
|
90
93
|
only two tones instead of six, both reusing `Chip`'s existing AA-safe colour pairs rather than the kit's PROVISIONAL
|
|
91
94
|
feedback tokens. The kit's six marketing tones are shown here captioned "kit, marketing only" and are not used in the
|
|
92
95
|
product.
|
|
96
|
+
|
|
97
|
+
0.5.0: North's count (style1.css:482; C6, C30) overturns the 2 px corners and the "no navy pill" rule: an 18 px navy pill
|
|
98
|
+
with cream 11 px numerals; the corner badge keeps 16 px.
|
|
99
|
+
|
|
100
|
+
## Markup
|
|
101
|
+
|
|
102
|
+
```html
|
|
103
|
+
<span class="ws-app-shell__row">Needs you <span class="ws-badge ws-badge--navy">5</span></span>
|
|
104
|
+
<span class="ws-badge-host">
|
|
105
|
+
<span class="ws-avatar" role="img" aria-label="Priya Nair, 3 unread">PN</span>
|
|
106
|
+
<span class="ws-badge ws-badge--needs" aria-hidden="true">3</span>
|
|
107
|
+
</span>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Styles
|
|
111
|
+
|
|
112
|
+
`design-system/components/Badge/style.css`: `ws-badge` with `--navy`, `--needs`, `--neutral`, `--dot`; `ws-badge-host`;
|
|
113
|
+
the marketing `ws-kit-badge` and its six tones: `ws-kit-badge--navy`, `ws-kit-badge--neutral`, `ws-kit-badge--brand`,
|
|
114
|
+
`ws-kit-badge--success`, `ws-kit-badge--error`, `ws-kit-badge--outline` (marketing pages only).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- @dsCard group="Feedback" height=
|
|
1
|
+
<!-- @dsCard group="Feedback" height=280 -->
|
|
2
2
|
<!doctype html>
|
|
3
3
|
<html>
|
|
4
4
|
<head>
|
|
@@ -12,25 +12,26 @@
|
|
|
12
12
|
<div class="ws-stack ws-stack--group">
|
|
13
13
|
|
|
14
14
|
<div class="ws-stack">
|
|
15
|
-
<span class="ws-muted">
|
|
15
|
+
<span class="ws-muted">A count: navy in a nav row, the needs tint on a face, sunk on an icon button (18 px pills)</span>
|
|
16
16
|
<div class="ws-cluster ws-cluster--component">
|
|
17
|
-
<span class="ws-badge-host">
|
|
18
|
-
<span class="ws-avatar">PN</span>
|
|
19
|
-
<span class="ws-badge ws-badge--needs" aria-hidden="true">3</span>
|
|
20
|
-
</span>
|
|
21
17
|
<div style="width: 180px;">
|
|
22
18
|
<span class="ws-app-shell__row ws-app-shell__row--current">
|
|
23
19
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 4h10M3 8h10M3 12h7"/></svg>
|
|
24
20
|
<span class="ws-app-shell__name">Needs you</span>
|
|
25
|
-
<span class="ws-badge ws-badge--
|
|
21
|
+
<span class="ws-badge ws-badge--navy">5</span>
|
|
26
22
|
</span>
|
|
27
23
|
</div>
|
|
24
|
+
<span class="ws-badge-host">
|
|
25
|
+
<span class="ws-avatar" role="img" aria-label="Priya Nair, 3 unread">PN</span>
|
|
26
|
+
<span class="ws-badge ws-badge--needs" aria-hidden="true">3</span>
|
|
27
|
+
</span>
|
|
28
28
|
<span class="ws-badge-host">
|
|
29
29
|
<button type="button" class="ws-icon-btn ws-icon-btn--outline" aria-label="Leads, 12 new">
|
|
30
|
-
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="5.5" r="2.5"/><path d="M3 13c0-2.5 2.2-4 5-4s5 1.5 5 4"/></svg>
|
|
30
|
+
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="8" cy="5.5" r="2.5"/><path d="M3 13c0-2.5 2.2-4 5-4s5 1.5 5 4"/></svg>
|
|
31
31
|
</button>
|
|
32
32
|
<span class="ws-badge ws-badge--neutral" aria-hidden="true">12</span>
|
|
33
33
|
</span>
|
|
34
|
+
<span class="ws-badge ws-badge--navy ws-pop" aria-hidden="true">128</span>
|
|
34
35
|
</div>
|
|
35
36
|
</div>
|
|
36
37
|
|
|
@@ -45,11 +46,13 @@
|
|
|
45
46
|
</div>
|
|
46
47
|
|
|
47
48
|
<div class="ws-stack">
|
|
48
|
-
<span class="ws-muted">Kit, marketing only: the six badgeVariants tones
|
|
49
|
-
<div class="ws-cluster
|
|
49
|
+
<span class="ws-muted">Kit, marketing only: the six badgeVariants tones, words on their AA step</span>
|
|
50
|
+
<div class="ws-cluster">
|
|
50
51
|
<span class="ws-kit-badge ws-kit-badge--navy">Navy</span>
|
|
51
52
|
<span class="ws-kit-badge ws-kit-badge--neutral">Neutral</span>
|
|
52
53
|
<span class="ws-kit-badge ws-kit-badge--brand">Brand</span>
|
|
54
|
+
</div>
|
|
55
|
+
<div class="ws-cluster">
|
|
53
56
|
<span class="ws-kit-badge ws-kit-badge--success">Success</span>
|
|
54
57
|
<span class="ws-kit-badge ws-kit-badge--error">Error</span>
|
|
55
58
|
<span class="ws-kit-badge ws-kit-badge--outline">Outline</span>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/* ---- Badge: a count or a dot on a face, a row or a button (and the kit's marketing pill). North's count (0.5.0, D1;
|
|
2
|
+
C6): an 18 px pill, 11 px semibold numerals that keep their width; navy for a count in the nav, the needs tint for
|
|
3
|
+
something that needs you, sunk for a plain count. It pops in when its number changes (re-key it with ws-pop). ---- */
|
|
4
|
+
.ws-badge { display: inline-flex; align-items: center; justify-content: center; min-width: calc(var(--space-md) + var(--space-3xs)); height: calc(var(--space-md) + var(--space-3xs)); padding: 0 var(--space-xxs-plus); border-radius: var(--radius-full); font-size: var(--ws-text-micro); line-height: var(--ws-leading-micro); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
5
|
+
.ws-badge--navy { background: var(--color-text-heading); color: var(--ws-on-act); }
|
|
6
|
+
.ws-badge--needs { background: var(--ws-tint-needs); color: var(--ws-word-needs); }
|
|
7
|
+
.ws-badge--neutral { background: var(--ws-surface-sunk); color: var(--color-text-heading); }
|
|
8
|
+
.ws-badge--dot { width: var(--space-xxs-plus); min-width: var(--space-xxs-plus); height: var(--space-xxs-plus); padding: 0; background: var(--ws-chart-under-half); }
|
|
9
|
+
.ws-badge.ws-pop { animation-timing-function: var(--ws-ease-pop); }
|
|
10
|
+
/* What a badge sits on: the badge on its top right corner, 4 px out, 16 tall so it covers less of a face, a 2 px ring
|
|
11
|
+
of the page keeping it apart. */
|
|
12
|
+
.ws-badge-host { position: relative; display: inline-flex; }
|
|
13
|
+
.ws-badge-host > .ws-badge { position: absolute; top: calc(var(--space-xxs) * -1); right: calc(var(--space-xxs) * -1); min-width: var(--space-md); height: var(--space-md); padding: 0 var(--space-xxs); box-shadow: 0 0 0 var(--ws-ring) var(--color-bg-page); }
|
|
14
|
+
.ws-badge-host > .ws-badge--dot { min-width: var(--space-xxs-plus); height: var(--space-xxs-plus); padding: 0; }
|
|
15
|
+
/* The kit's badge, marketing pages only: a pill, 12 px medium, six tones. Words take their AA step on their own tint;
|
|
16
|
+
the outline is drawn inside, so every tone is the same size. */
|
|
17
|
+
.ws-kit-badge { display: inline-flex; align-items: center; gap: var(--space-xxs); height: var(--space-lg); padding: 0 var(--space-xs-plus); border-radius: var(--radius-full); font-size: var(--font-size-2xs); line-height: var(--space-md); font-weight: 500; white-space: nowrap; }
|
|
18
|
+
.ws-kit-badge--navy { background: color-mix(in srgb, var(--color-text-heading) 10%, transparent); color: var(--color-text-heading); }
|
|
19
|
+
.ws-kit-badge--neutral { background: var(--ws-track); color: var(--color-text-heading); }
|
|
20
|
+
.ws-kit-badge--brand { background: color-mix(in srgb, var(--ws-chart-under-half) 12%, transparent); color: var(--color-text-brand-strong); }
|
|
21
|
+
.ws-kit-badge--success { background: var(--color-feedback-success-bg); color: var(--color-text-success-strong); }
|
|
22
|
+
.ws-kit-badge--error { background: color-mix(in srgb, var(--color-feedback-error) 14%, transparent); color: var(--color-text-error-strong); }
|
|
23
|
+
.ws-kit-badge--outline { background: transparent; color: var(--color-text-body); box-shadow: inset 0 0 0 var(--ws-line) var(--color-border-hairline); }
|
|
@@ -14,13 +14,14 @@ A full-width notice for a state of the whole page or card, in one of four quiet
|
|
|
14
14
|
|
|
15
15
|
## Anatomy
|
|
16
16
|
|
|
17
|
-
- Shell: full width of its container, `--space-sm`
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
17
|
+
- Shell: full width of its container, 8 / 12 px inside (`--space-xs`, `--space-sm`), 8 px corners (`--ws-radius-card`),
|
|
18
|
+
a row of a mark, words, an act and an optional close, 8 apart.
|
|
19
|
+
- One band: everything is centred on a 28 px band at the top (40 on touch): the mark, the first line of words and each
|
|
20
|
+
act. When the words wrap on a phone the mark and the acts stay level with the first line.
|
|
21
|
+
- Mark: 16 px line icon at the left, in the tone's words colour.
|
|
22
|
+
- Words: 14 / 20, two parts: the first sentence navy medium, the rest body grey.
|
|
23
|
+
- Act: a `text` button or a `well` small (28 px) button, at the right.
|
|
24
|
+
- Optional close: a 24 px `xs` ghost `IconButton` x (a 40 px target on touch).
|
|
24
25
|
|
|
25
26
|
## Variants and sizes
|
|
26
27
|
|
|
@@ -37,7 +38,9 @@ There is one size; it spans whatever width its container gives it.
|
|
|
37
38
|
|
|
38
39
|
- **The four tones:** as the table above, each shown with its own icon, words and act.
|
|
39
40
|
- **With a close act:** the `x` icon button sits at the far right.
|
|
41
|
+
- **Long words / narrow:** the words wrap; the mark and the acts stay on the first line's band.
|
|
40
42
|
- **Dismissed:** the banner is removed entirely; nothing is drawn in its place.
|
|
43
|
+
- **Dark:** each tint and its words take their dark values; every pair stays AA.
|
|
41
44
|
|
|
42
45
|
## Behaviour
|
|
43
46
|
|
|
@@ -59,7 +62,7 @@ There is one size; it spans whatever width its container gives it.
|
|
|
59
62
|
| `tone` | `'neutral' \| 'needs' \| 'positive' \| 'working'` | the look, default `neutral` |
|
|
60
63
|
| `icon` | `IconName` | the 16 px icon at the left |
|
|
61
64
|
| `children` | `ReactNode` | the words |
|
|
62
|
-
| `action` | `ReactNode` | the text or
|
|
65
|
+
| `action` | `ReactNode` | the `text` or `well` small button at the right |
|
|
63
66
|
| `onDismiss` | `() => void` | present only when the close act shows |
|
|
64
67
|
|
|
65
68
|
## Rules
|
|
@@ -74,5 +77,23 @@ There is one size; it spans whatever width its container gives it.
|
|
|
74
77
|
|
|
75
78
|
**Derived from**: 10-patterns.md's one ad hoc case ("Offline, a muted line at the top of the stage"), generalized into
|
|
76
79
|
four tones built on the system's own wash tokens (`ws-tint-needs`, `ws-tint-live`, `ws-card-tint-blue`,
|
|
77
|
-
`ws-surface-sunk`) and `Button`'s
|
|
80
|
+
`ws-surface-sunk`) and `Button`'s small size for its act. **Precedent**: an intentional addition decided
|
|
78
81
|
from the system, 26 Oct 2026.
|
|
82
|
+
|
|
83
|
+
0.5.0: the band fix (the mark and the acts floated to the middle of wrapped words).
|
|
84
|
+
|
|
85
|
+
## Markup
|
|
86
|
+
|
|
87
|
+
```html
|
|
88
|
+
<div class="ws-banner ws-banner--needs" role="alert">
|
|
89
|
+
<svg class="ws-banner__mark" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2.3 14.6 13.5H1.4L8 2.3Z"/><path d="M8 6.4v3"/><path d="M8 11.5v.05"/></svg>
|
|
90
|
+
<span class="ws-banner__words"><span class="ws-banner__lead">You're offline.</span> <span class="ws-banner__rest">Changes will send once you're back.</span></span>
|
|
91
|
+
<button type="button" class="ws-btn ws-btn--well ws-btn--sm">Retry</button>
|
|
92
|
+
<button type="button" class="ws-icon-btn ws-icon-btn--xs" aria-label="Dismiss"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg></button>
|
|
93
|
+
</div>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Styles
|
|
97
|
+
|
|
98
|
+
`design-system/components/Banner/style.css`: `ws-banner` with `--neutral`, `--needs`, `--positive`, `--working`;
|
|
99
|
+
`__mark`, `__words`, `__lead`, `__rest`.
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<div class="ws-banner ws-banner--needs" role="alert">
|
|
26
26
|
<svg class="ws-banner__mark" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M8 2.3 14.6 13.5H1.4L8 2.3Z"/><path d="M8 6.4v3"/><path d="M8 11.5v.05"/></svg>
|
|
27
27
|
<span class="ws-banner__words"><span class="ws-banner__lead">You're offline.</span> <span class="ws-banner__rest">Changes will send once you're back.</span></span>
|
|
28
|
-
<button type="button" class="ws-btn ws-btn--
|
|
28
|
+
<button type="button" class="ws-btn ws-btn--well ws-btn--sm">Retry</button>
|
|
29
29
|
<button type="button" class="ws-icon-btn ws-icon-btn--xs" aria-label="Dismiss">
|
|
30
30
|
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg>
|
|
31
31
|
</button>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* ---- Banner: a full-width line of news on a tint, its mark, its words and at most one act (and a close). Everything
|
|
2
|
+
sits on one 28 px band at the top (40 on touch): the mark, the first line of words and each act are centred on it, so
|
|
3
|
+
when the words wrap on a phone the mark and the acts stay with the first line instead of floating in the middle. ---- */
|
|
4
|
+
.ws-banner { --ws-banner-band: var(--ws-control-h-sm); display: flex; align-items: flex-start; gap: var(--space-xs); width: 100%; padding: var(--space-xs) var(--space-sm); border-radius: var(--ws-radius-card); }
|
|
5
|
+
.ws-banner__mark { flex-shrink: 0; width: var(--ws-icon); height: var(--ws-icon); margin-block: calc((var(--ws-banner-band) - var(--ws-icon)) / 2); }
|
|
6
|
+
.ws-banner__words { flex: 1; min-width: 0; padding-block: calc((var(--ws-banner-band) - var(--space-md) - var(--space-xxs)) / 2); font-size: var(--ws-text-body); line-height: calc(var(--space-md) + var(--space-xxs)); }
|
|
7
|
+
.ws-banner > :is(.ws-btn--link, .ws-btn--text) { min-height: var(--space-lg); }
|
|
8
|
+
.ws-banner > :is(.ws-btn--link, .ws-btn--text, .ws-icon-btn--xs) { margin-block: calc((var(--ws-banner-band) - var(--space-lg)) / 2); }
|
|
9
|
+
.ws-banner__lead { font-weight: 500; color: var(--color-text-heading); }
|
|
10
|
+
.ws-banner__rest { color: var(--color-text-body); }
|
|
11
|
+
.ws-banner--neutral { background: var(--ws-surface-sunk); }
|
|
12
|
+
.ws-banner--neutral .ws-banner__mark { color: var(--color-text-muted); }
|
|
13
|
+
.ws-banner--needs { background: var(--ws-tint-needs); }
|
|
14
|
+
.ws-banner--needs .ws-banner__mark, .ws-banner--needs .ws-banner__lead { color: var(--ws-word-needs); }
|
|
15
|
+
.ws-banner--positive { background: var(--ws-tint-live); }
|
|
16
|
+
.ws-banner--positive .ws-banner__mark, .ws-banner--positive .ws-banner__lead { color: var(--ws-word-positive); }
|
|
17
|
+
.ws-banner--working { background: var(--ws-card-tint-blue); }
|
|
18
|
+
.ws-banner--working .ws-banner__mark, .ws-banner--working .ws-banner__lead { color: var(--color-text-interactive); }
|
|
19
|
+
@media (pointer: coarse) { .ws-banner { --ws-banner-band: var(--ws-touch); } }
|