@cahyo-dimas/freeday 1.53.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +657 -535
- package/COMPONENTS.md +153 -135
- package/README.id.md +46 -45
- package/README.md +46 -43
- package/USAGE.md +39 -35
- package/adapters/blazor/FdyAppShell.razor +50 -0
- package/adapters/blazor/FdyAppShell.razor.cs +114 -0
- package/adapters/blazor/FdyCfl.razor.cs +1 -1
- package/adapters/blazor/FdyChart.razor +1 -1
- package/adapters/blazor/FdyChart.razor.cs +3 -3
- package/adapters/blazor/FdyChartSeries.cs +1 -1
- package/adapters/blazor/FdyCombo.razor.cs +2 -2
- package/adapters/blazor/FdyModal.razor +1 -1
- package/adapters/blazor/FdyTable.razor.cs +7 -7
- package/adapters/blazor/FdyTableFilter.razor +1 -1
- package/adapters/blazor/FdyTableFooter.razor +1 -1
- package/adapters/blazor/FdyTableFooter.razor.cs +4 -4
- package/adapters/blazor/Freeday.Blazor.csproj +1 -1
- package/adapters/blazor/FreedayComponentBase.cs +1 -1
- package/adapters/blazor/TableModel.cs +3 -3
- package/adapters/blazor/TableTypes.cs +3 -3
- package/adapters/blazor/freeday-blazor.js +5 -5
- package/adapters/core/app-shell.d.ts +30 -0
- package/adapters/core/app-shell.js +134 -0
- package/adapters/core/table-model.d.ts +2 -2
- package/adapters/core/table-model.js +8 -8
- package/adapters/react/components/FdyAppShell.tsx +159 -0
- package/adapters/react/components/FdyAutocomplete.tsx +1 -1
- package/adapters/react/components/FdyCascade.tsx +1 -1
- package/adapters/react/components/FdyCfl.tsx +11 -11
- package/adapters/react/components/FdyDateRange.tsx +2 -2
- package/adapters/react/components/FdyDatepicker.tsx +5 -5
- package/adapters/react/components/FdyDrawer.tsx +1 -1
- package/adapters/react/components/FdyModal.tsx +2 -2
- package/adapters/react/components/FdyTable.tsx +7 -7
- package/adapters/react/components/FdyTableFilter.tsx +1 -1
- package/adapters/react/components/FdyTableFooter.tsx +5 -5
- package/adapters/react/index.d.ts +2 -1
- package/adapters/react/index.js +1 -0
- package/adapters/react/useFreeday.js +3 -3
- package/adapters/vue/components/FdyAppShell.vue +160 -0
- package/adapters/vue/components/FdyAutocomplete.vue +1 -1
- package/adapters/vue/components/FdyCascade.vue +1 -1
- package/adapters/vue/components/FdyCfl.vue +8 -8
- package/adapters/vue/components/FdyChart.vue +2 -2
- package/adapters/vue/components/FdyCombo.vue +1 -1
- package/adapters/vue/components/FdyDateRange.vue +2 -2
- package/adapters/vue/components/FdyDatepicker.vue +5 -5
- package/adapters/vue/components/FdyDrawer.vue +3 -3
- package/adapters/vue/components/FdyModal.vue +3 -3
- package/adapters/vue/components/FdyTable.vue +7 -7
- package/adapters/vue/components/FdyTableFilter.vue +1 -1
- package/adapters/vue/components/FdyTableFooter.vue +5 -5
- package/adapters/vue/index.d.ts +3 -2
- package/adapters/vue/index.js +1 -0
- package/adapters/vue/useFreeday.js +3 -3
- package/dist/asset.d.ts +3 -3
- package/dist/freeday-app-shell.js +49 -9
- package/dist/freeday-autocomplete.js +2 -2
- package/dist/freeday-breakpoint.js +1 -1
- package/dist/freeday-carousel.js +5 -5
- package/dist/freeday-cascade.js +7 -7
- package/dist/freeday-cfl.js +9 -9
- package/dist/freeday-chart.js +11 -11
- package/dist/freeday-chip.js +3 -3
- package/dist/freeday-datepicker.js +11 -7
- package/dist/freeday-datetime.js +5 -5
- package/dist/freeday-drawer.js +1 -1
- package/dist/freeday-form.js +14 -14
- package/dist/freeday-mask.js +6 -6
- package/dist/freeday-menu.js +1 -1
- package/dist/freeday-number.js +3 -3
- package/dist/freeday-popover.js +3 -3
- package/dist/freeday-rating.js +2 -2
- package/dist/freeday-select.js +5 -5
- package/dist/freeday-slider.js +2 -2
- package/dist/freeday-stepper.js +7 -7
- package/dist/freeday-table.js +15 -15
- package/dist/freeday-tabs.js +1 -1
- package/dist/freeday-timepicker.js +2 -2
- package/dist/freeday-toast.js +8 -8
- package/dist/freeday-tree.js +3 -3
- package/dist/freeday-upload.js +25 -25
- package/dist/freeday.bundle.css +176 -176
- package/dist/freeday.css +170 -170
- package/dist/freeday.d.ts +2 -2
- package/dist/freeday.js +202 -158
- package/dist/freeday.tokens.css +5 -5
- package/docs/agent-onboarding.md +42 -42
- package/docs/getting-started.md +35 -35
- package/docs/integrations.md +18 -18
- package/docs/reference-screen.html +14 -14
- package/package.json +5 -4
- package/src/base.css +6 -6
- package/src/components/accordion.css +4 -4
- package/src/components/alert.css +1 -1
- package/src/components/app-shell.css +10 -10
- package/src/components/appbar.css +3 -3
- package/src/components/autocomplete.css +1 -1
- package/src/components/avatar.css +3 -3
- package/src/components/badge.css +5 -5
- package/src/components/breadcrumb.css +1 -1
- package/src/components/breakpoints.css +4 -4
- package/src/components/button.css +13 -13
- package/src/components/card.css +8 -8
- package/src/components/carousel.css +2 -2
- package/src/components/cascade.css +2 -2
- package/src/components/cfl.css +4 -4
- package/src/components/chart.css +3 -3
- package/src/components/chip.css +2 -2
- package/src/components/combo.css +3 -3
- package/src/components/composition.css +7 -7
- package/src/components/datepicker.css +6 -6
- package/src/components/datetimepicker.css +1 -1
- package/src/components/description-list.css +1 -1
- package/src/components/divider.css +1 -1
- package/src/components/drawer.css +1 -1
- package/src/components/file-upload.css +5 -5
- package/src/components/filterbar.css +7 -7
- package/src/components/form-grid.css +2 -2
- package/src/components/input-group.css +6 -6
- package/src/components/input.css +6 -6
- package/src/components/kbd.css +1 -1
- package/src/components/list.css +6 -6
- package/src/components/menu.css +3 -3
- package/src/components/modal.css +1 -1
- package/src/components/pagination.css +1 -1
- package/src/components/progress.css +1 -1
- package/src/components/rating.css +2 -2
- package/src/components/selection.css +4 -4
- package/src/components/skeleton.css +1 -1
- package/src/components/slider.css +1 -1
- package/src/components/spinner.css +1 -1
- package/src/components/states.css +1 -1
- package/src/components/stepper.css +2 -2
- package/src/components/table.css +15 -15
- package/src/components/tabs.css +3 -3
- package/src/components/timeline.css +1 -1
- package/src/components/timepicker.css +1 -1
- package/src/components/toast.css +3 -3
- package/src/components/tooltip.css +1 -1
- package/src/components/tree.css +2 -2
- package/tokens/breakpoints.d.ts +2 -2
- package/tokens/breakpoints.mjs +3 -3
- package/tokens/tokens.json +1 -1
package/dist/freeday.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/* Freeday components
|
|
2
|
-
/* Freeday base layer
|
|
1
|
+
/* Freeday components. GENERATED by tokens/build.mjs. Do not edit by hand. */
|
|
2
|
+
/* Freeday base layer: reset ringan + default via token */
|
|
3
3
|
*, *::before, *::after { box-sizing: border-box; }
|
|
4
4
|
html { -webkit-text-size-adjust: 100%; }
|
|
5
5
|
body {
|
|
@@ -17,21 +17,21 @@ a { color: var(--color-primary); }
|
|
|
17
17
|
:where(button, input, select, textarea) { font: inherit; }
|
|
18
18
|
/* Hidden from sight, kept for assistive tech. `clip` hides PAINTING, not LAYOUT: the box still
|
|
19
19
|
* has a position, and because it is position:absolute its containing block is the nearest
|
|
20
|
-
* POSITIONED ancestor
|
|
20
|
+
* POSITIONED ancestor, and with none, the initial containing block (the document itself).
|
|
21
21
|
*
|
|
22
22
|
* That matters, because `overflow` only clips a descendant whose containing block is inside the
|
|
23
23
|
* overflow box. So a hidden span in a horizontally scrolling table (the kit's own recommended way
|
|
24
|
-
* to name an icon button) parks at its static position
|
|
24
|
+
* to name an icon button) parks at its static position, possibly thousands of px to the right —
|
|
25
25
|
* and drags the whole DOCUMENT sideways: invisible, and immune to `overflow-x:hidden` on every
|
|
26
26
|
* wrapper. Measured before the fix: 1351px of phantom page scroll from 11 spans.
|
|
27
27
|
*
|
|
28
|
-
* The fix is not here
|
|
28
|
+
* The fix is not here but on the containers: every clipping/scrolling container in the kit
|
|
29
29
|
* that holds consumer markup declares `position:relative` so it becomes the containing block for
|
|
30
30
|
* its own out-of-flow content (see test/css.test.mjs, which will not let a new one skip it).
|
|
31
|
-
* Note the measurement trap: documentElement.scrollWidth sees it, body.scrollWidth does not
|
|
31
|
+
* Note the measurement trap: documentElement.scrollWidth sees it, body.scrollWidth does not,
|
|
32
32
|
* the honest check is `window.scrollTo(9999, 0)` then reading `window.scrollX`. */
|
|
33
33
|
.fdy-visually-hidden { position: absolute !important; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
|
|
34
|
-
/* Opt-in list reset. base.css is intentionally a *light* reset
|
|
34
|
+
/* Opt-in list reset. base.css is intentionally a *light* reset: it does NOT strip list/paragraph
|
|
35
35
|
* margins (see the kit's own list components, which each reset themselves). If you run a utility
|
|
36
36
|
* framework with its preflight OFF (the recommended pairing), a semantic <ul>/<ol> keeps native
|
|
37
37
|
* bullets + a 40px indent; add this class (or a Freeday list component) to normalise it. */
|
|
@@ -40,10 +40,10 @@ a { color: var(--color-primary); }
|
|
|
40
40
|
*, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
/* Freeday
|
|
44
|
-
/* position:relative
|
|
43
|
+
/* Freeday. Accordion (native <details>/<summary>, zero-JS) */
|
|
44
|
+
/* position:relative, containing block for out-of-flow panel content (see base.css). NOT redundant
|
|
45
45
|
with the panel's reveal animation: that animation gives the panel a transform (which happens to
|
|
46
|
-
make it a containing block too), but it lives behind prefers-reduced-motion:no-preference
|
|
46
|
+
make it a containing block too), but it lives behind prefers-reduced-motion:no-preference, so
|
|
47
47
|
without this line the escape bug appears ONLY for readers who asked for reduced motion. */
|
|
48
48
|
.fdy-accordion{position:relative;border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;background:var(--color-surface);}
|
|
49
49
|
.fdy-accordion__item + .fdy-accordion__item{border-top:var(--bw) solid var(--color-border);}
|
|
@@ -55,10 +55,10 @@ a { color: var(--color-primary); }
|
|
|
55
55
|
.fdy-accordion__item > summary:focus-visible{outline:none;box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
|
|
56
56
|
.fdy-accordion__panel{padding:0 var(--space-4) var(--space-4);color:var(--color-text-muted);font-size:var(--text-sm);line-height:var(--leading-normal);}
|
|
57
57
|
/* Content reveal on open (fade + rise). Native <details> can't animate its own height cross-
|
|
58
|
-
* browser without JS, so we animate the revealed panel instead
|
|
58
|
+
* browser without JS, so we animate the revealed panel instead, cheap, safe, reduced-motion gated. */
|
|
59
59
|
@media (prefers-reduced-motion:no-preference){.fdy-accordion__item[open] > .fdy-accordion__panel{animation:fdy-combo-in var(--dur-base) var(--ease-standard);}}
|
|
60
60
|
|
|
61
|
-
/* Freeday
|
|
61
|
+
/* Freeday. Alert (inline) */
|
|
62
62
|
.fdy-alert{display:flex;gap:var(--space-3);padding:var(--space-3) var(--space-4);border-radius:var(--radius-md);border:var(--bw) solid var(--color-border);font-size:var(--text-sm);color:var(--color-text);background:var(--color-surface-2);}
|
|
63
63
|
.fdy-alert__icon{flex:none;display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;margin-top:.05rem;}
|
|
64
64
|
.fdy-alert__icon svg{width:100%;height:100%;}
|
|
@@ -77,17 +77,17 @@ a { color: var(--color-primary); }
|
|
|
77
77
|
.fdy-alert--danger{background:var(--color-danger-soft);border-color:color-mix(in srgb,var(--color-danger) 26%,transparent);}
|
|
78
78
|
.fdy-alert--danger .fdy-alert__title,.fdy-alert--danger .fdy-alert__icon{color:var(--color-danger-strong);}
|
|
79
79
|
|
|
80
|
-
/* Freeday
|
|
80
|
+
/* Freeday. App Shell.
|
|
81
81
|
* Natural document scroll: the shell grows with content and the PAGE scrolls,
|
|
82
82
|
* while the sidebar and topbar are position:sticky so they stay in view. No
|
|
83
83
|
* fixed-height / internal-scroll trickery (that leaves a white gap if the host
|
|
84
84
|
* page can still scroll). Structure: .fdy-app > (.fdy-app__sidebar, .fdy-app__content
|
|
85
85
|
* > (.fdy-app__topbar, .fdy-app__main)). The __content wrapper gives the sticky
|
|
86
|
-
* topbar a tall containing block so it can travel
|
|
86
|
+
* topbar a tall containing block so it can travel, a bare grid cell can't. */
|
|
87
87
|
.fdy-app{display:flex;align-items:stretch;min-height:100vh;background:var(--color-surface-2);}
|
|
88
88
|
.fdy-app__sidebar{position:sticky;top:0;align-self:flex-start;height:100vh;overflow:hidden;flex:none;width:15.5rem;box-sizing:border-box;background:var(--color-surface);border-right:var(--bw) solid var(--color-border);display:flex;flex-direction:column;transition:width var(--dur-slow) var(--ease-standard),border-right-width var(--dur-slow) var(--ease-standard);}
|
|
89
89
|
.fdy-app__content{flex:1;min-width:0;display:flex;flex-direction:column;}
|
|
90
|
-
/* Brand is a fixed header
|
|
90
|
+
/* Brand is a fixed header, same height as the topbar so they align, and only
|
|
91
91
|
* the nav below it scrolls. Bare text (no children) renders as one bold
|
|
92
92
|
* display line via the rules on .fdy-app__brand itself. For a mark + two-line
|
|
93
93
|
* title/subtitle, add structured children mirroring .fdy-appbar__brand:
|
|
@@ -103,10 +103,10 @@ a { color: var(--color-primary); }
|
|
|
103
103
|
.fdy-app__brand-title{display:block;font-family:var(--font-display);font-weight:var(--weight-bold);font-size:var(--text-lg);color:var(--color-text);margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
104
104
|
.fdy-app__brand-subtitle{display:block;font-size:var(--text-xs);font-weight:var(--weight-regular);color:var(--color-text-muted);margin:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
105
105
|
.fdy-nav{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;display:flex;flex-direction:column;gap:2px;padding:var(--space-3) var(--space-3) var(--space-6);}
|
|
106
|
-
/* Horizontal variant
|
|
106
|
+
/* Horizontal variant, the SAME nav links laid out as a row, for a top-nav application (one that
|
|
107
107
|
* puts its primary navigation in `.fdy-appbar` / `.fdy-app__topbar` and has no sidebar). Deliberately
|
|
108
108
|
* a modifier and not a new block: the item, its states and `aria-current="page"` are unchanged. That
|
|
109
|
-
* is the point
|
|
109
|
+
* is the point, a navigation link is a link marked `aria-current`, never `aria-selected` (invalid
|
|
110
110
|
* ARIA on an anchor) and never a tab role, so borrowing `.fdy-tabs` for routes half-adopts a contract
|
|
111
111
|
* (roving tabindex, a panel per tab) that route links do not honour. position:relative because the
|
|
112
112
|
* row scrolls; see base.css on why a scroller must be a containing block. */
|
|
@@ -118,7 +118,7 @@ a { color: var(--color-primary); }
|
|
|
118
118
|
.fdy-nav__item[aria-current="page"]{background:var(--color-primary-soft);color:var(--color-primary-strong);font-weight:var(--weight-semibold);}
|
|
119
119
|
.fdy-nav__item:focus-visible{outline:none;box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
|
|
120
120
|
/* Sidebar-menu extras: per-node icon (leading), count badge (trailing), and a
|
|
121
|
-
* text label that truncates. Any of these are optional
|
|
121
|
+
* text label that truncates. Any of these are optional, a bare text item works. */
|
|
122
122
|
.fdy-nav__icon{flex:none;display:inline-flex;width:1.15rem;height:1.15rem;}
|
|
123
123
|
.fdy-nav__icon svg{display:block;width:100%;height:100%;}
|
|
124
124
|
.fdy-nav__label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
@@ -128,7 +128,7 @@ a { color: var(--color-primary); }
|
|
|
128
128
|
.fdy-nav__group{margin:0;}
|
|
129
129
|
.fdy-nav__group + .fdy-nav__group,.fdy-nav__item + .fdy-nav__group{border-top:var(--bw) solid var(--color-border-muted);margin-top:var(--space-1);padding-top:var(--space-1);}
|
|
130
130
|
/* Prose, and a control name: this is the <summary> of a nav group, at --text-xs uppercase. It is
|
|
131
|
-
the second class found spending the decorative ink on type (after .fdy-eyebrow)
|
|
131
|
+
the second class found spending the decorative ink on type (after .fdy-eyebrow), and it was
|
|
132
132
|
already brightening to --color-text-muted on hover, which is the rule admitting its resting
|
|
133
133
|
state was too faint to read. */
|
|
134
134
|
.fdy-nav__grouplabel{list-style:none;cursor:pointer;display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);padding:var(--space-3) var(--space-3) var(--space-1);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:var(--tracking-wide);font-weight:var(--weight-semibold);color:var(--color-text-muted);}
|
|
@@ -137,7 +137,7 @@ a { color: var(--color-primary); }
|
|
|
137
137
|
.fdy-nav__grouplabel:focus-visible{outline:none;border-radius:var(--radius-sm);box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
|
|
138
138
|
.fdy-nav__grouplabel::after{content:"";flex:none;width:.45rem;height:.45rem;margin-right:var(--space-1);border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(-45deg);transition:transform var(--dur-fast) var(--ease-standard);opacity:.7;}
|
|
139
139
|
.fdy-nav__group[open] > .fdy-nav__grouplabel::after{transform:rotate(45deg);}
|
|
140
|
-
/* --flat: calmer, non-collapsible grouped nav
|
|
140
|
+
/* --flat: calmer, non-collapsible grouped nav, hide the caret, drop the
|
|
141
141
|
* inter-group divider and the collapse cursor. Works on native <details> groups
|
|
142
142
|
* (caret just hidden) and on static <div>/<p> groups (never collapsible). */
|
|
143
143
|
.fdy-nav--flat .fdy-nav__grouplabel::after{display:none;}
|
|
@@ -150,7 +150,7 @@ a { color: var(--color-primary); }
|
|
|
150
150
|
.fdy-nav__tree > summary .fdy-nav__caret{flex:none;margin-left:auto;width:.4rem;height:.4rem;border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(-45deg);transition:transform var(--dur-fast) var(--ease-standard);opacity:.6;}
|
|
151
151
|
.fdy-nav__tree[open] > summary .fdy-nav__caret{transform:rotate(45deg);}
|
|
152
152
|
.fdy-nav__sub{display:flex;flex-direction:column;gap:2px;padding-left:calc(var(--space-3) + 1.15rem);margin-top:2px;}
|
|
153
|
-
/* Collapse toggle (hamburger)
|
|
153
|
+
/* Collapse toggle (hamburger), sits in the topbar; collapses the sidebar. */
|
|
154
154
|
.fdy-app__navtoggle{display:inline-flex;align-items:center;justify-content:center;width:2.25rem;height:2.25rem;flex:none;padding:0;border:0;border-radius:var(--radius-md);background:transparent;color:var(--color-text-muted);cursor:pointer;transition:background var(--dur-fast) var(--ease-standard),color var(--dur-fast) var(--ease-standard);}
|
|
155
155
|
.fdy-app__navtoggle:hover{background:var(--color-surface-2);color:var(--color-text);}
|
|
156
156
|
.fdy-app__navtoggle:focus-visible{outline:none;box-shadow:0 0 0 2px var(--color-primary);}
|
|
@@ -163,7 +163,7 @@ a { color: var(--color-primary); }
|
|
|
163
163
|
.fdy-app__main{flex:1;padding:var(--space-8);}
|
|
164
164
|
.fdy-skip{position:absolute;left:var(--space-2);top:var(--space-2);background:var(--color-primary);color:var(--color-on-primary);padding:var(--space-2) var(--space-4);border-radius:var(--radius-md);z-index:100;transform:translateY(-250%);transition:transform var(--dur-fast) var(--ease-standard);}
|
|
165
165
|
.fdy-skip:focus{transform:translateY(0);}
|
|
166
|
-
/* --static: for a bounded/embedded shell (a demo box, not the whole page)
|
|
166
|
+
/* --static: for a bounded/embedded shell (a demo box, not the whole page),
|
|
167
167
|
* drop sticky so nothing escapes the box; sidebar stretches to fill it. */
|
|
168
168
|
.fdy-app--static .fdy-app__sidebar{position:static;height:auto;align-self:stretch;}
|
|
169
169
|
.fdy-app--static .fdy-app__topbar{position:static;}
|
|
@@ -179,7 +179,7 @@ a { color: var(--color-primary); }
|
|
|
179
179
|
}
|
|
180
180
|
@media (prefers-reduced-motion:reduce){.fdy-app__sidebar{transition:none;}}
|
|
181
181
|
|
|
182
|
-
/* Freeday
|
|
182
|
+
/* Freeday. App bar (standalone top bar: brand · actions · spacer).
|
|
183
183
|
* Distinct from .fdy-app__topbar (which is bound to the shell grid). Use anywhere;
|
|
184
184
|
* sticky / elevated / dense / coloured via modifiers. Pure CSS. */
|
|
185
185
|
.fdy-appbar{display:flex;align-items:center;gap:var(--space-3);height:var(--space-16);padding:0 var(--space-4);background:var(--color-surface);color:var(--color-text);border-bottom:var(--bw) solid var(--color-border);}
|
|
@@ -192,19 +192,19 @@ a { color: var(--color-primary); }
|
|
|
192
192
|
.fdy-appbar__spacer{flex:1;}
|
|
193
193
|
.fdy-appbar__actions{display:inline-flex;align-items:center;gap:var(--space-1);}
|
|
194
194
|
/* Primary navigation inside the bar: `<nav class="fdy-nav fdy-nav--horizontal">` with
|
|
195
|
-
`.fdy-nav__item` links (see app-shell.css). The bar ships no link class of its own
|
|
195
|
+
`.fdy-nav__item` links (see app-shell.css). The bar ships no link class of its own, a nav link
|
|
196
196
|
is the same component whether the nav is a sidebar column or a top row. */
|
|
197
197
|
/* Controls on a coloured app bar go on-colour (icons stay legible, hover is a light wash) */
|
|
198
198
|
.fdy-appbar--primary .fdy-btn--ghost{background:transparent;border-color:transparent;color:var(--color-on-primary);box-shadow:none;}
|
|
199
199
|
.fdy-appbar--primary .fdy-btn--ghost:hover{background:color-mix(in srgb,var(--color-on-primary) 16%,transparent);color:var(--color-on-primary);transform:none;}
|
|
200
200
|
/* Nav links on a coloured bar: full on-colour ink in every state (a dimmed variant would trade away
|
|
201
201
|
the contrast the bar's own token pair guarantees). The current page is carried by the background
|
|
202
|
-
wash plus the semibold weight the base rule already applies
|
|
202
|
+
wash plus the semibold weight the base rule already applies, and by `aria-current` itself. */
|
|
203
203
|
.fdy-appbar--primary .fdy-nav__item{color:var(--color-on-primary);}
|
|
204
204
|
.fdy-appbar--primary .fdy-nav__item:hover{background:color-mix(in srgb,var(--color-on-primary) 16%,transparent);color:var(--color-on-primary);}
|
|
205
205
|
.fdy-appbar--primary .fdy-nav__item[aria-current="page"]{background:color-mix(in srgb,var(--color-on-primary) 24%,transparent);color:var(--color-on-primary);}
|
|
206
206
|
|
|
207
|
-
/* Freeday
|
|
207
|
+
/* Freeday. Autocomplete (text input + filtered suggestion listbox, APG editable combobox).
|
|
208
208
|
* Enhanced by freeday-autocomplete.js. */
|
|
209
209
|
.fdy-autocomplete{position:relative;display:block;max-width:22rem;}
|
|
210
210
|
.fdy-autocomplete__listbox{position:absolute;top:calc(100% + var(--space-1));left:0;z-index:120;min-width:100%;max-height:16rem;overflow-y:auto;margin:0;padding:var(--space-1);list-style:none;background:var(--color-surface);color:var(--color-text);border:var(--bw) solid var(--color-border-strong);border-radius:var(--radius-md);box-shadow:var(--shadow-3);}
|
|
@@ -217,20 +217,20 @@ a { color: var(--color-primary); }
|
|
|
217
217
|
.fdy-autocomplete__empty[hidden]{display:none;}
|
|
218
218
|
@media (prefers-reduced-motion:no-preference){.fdy-autocomplete__listbox:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
219
219
|
|
|
220
|
-
/* Freeday
|
|
220
|
+
/* Freeday. Avatar */
|
|
221
221
|
.fdy-avatar{display:inline-flex;align-items:center;justify-content:center;flex:none;width:2.5rem;height:2.5rem;border-radius:var(--radius-full);background:var(--color-primary-soft);color:var(--color-primary-strong);font-family:var(--font-body);font-weight:var(--weight-semibold);font-size:var(--text-sm);line-height:1;overflow:hidden;user-select:none;}
|
|
222
222
|
.fdy-avatar img{width:100%;height:100%;object-fit:cover;}
|
|
223
223
|
/* --xs exists for an avatar INSIDE a control: `.fdy-btn--sm` is exactly 2rem tall, which is also
|
|
224
224
|
`--sm`'s size, so a small avatar in a small button fills it edge to edge and the ghost border
|
|
225
225
|
crosses the circle. 1.5rem leaves the same ~4px breathing room the kit's controls give their
|
|
226
|
-
icons. Uses `--text-xs` (the smallest type token the kit has
|
|
226
|
+
icons. Uses `--text-xs` (the smallest type token the kit has, there is no `2xs`). */
|
|
227
227
|
.fdy-avatar--xs{width:1.5rem;height:1.5rem;font-size:var(--text-xs);}
|
|
228
228
|
.fdy-avatar--sm{width:2rem;height:2rem;font-size:var(--text-xs);}
|
|
229
229
|
.fdy-avatar--lg{width:3.5rem;height:3.5rem;font-size:var(--text-lg);}
|
|
230
230
|
.fdy-avatar-group{display:inline-flex;}
|
|
231
231
|
.fdy-avatar-group .fdy-avatar{border:2px solid var(--color-surface);margin-left:calc(var(--space-2)*-1);}
|
|
232
232
|
.fdy-avatar-group .fdy-avatar:first-child{margin-left:0;}
|
|
233
|
-
/* Decorative identity tones
|
|
233
|
+
/* Decorative identity tones, a soft tint of a categorical chart colour with a theme-aware,
|
|
234
234
|
* text-leaning foreground so initials keep WCAG AA (verified >=4.5:1 in light & dark by
|
|
235
235
|
* test/contrast.test.mjs). Use to disambiguate same-initial avatars (e.g. many "PT …" names):
|
|
236
236
|
* hash a stable 1..8 index off the FULL name in your app, then apply .fdy-avatar--tone-<n>. */
|
|
@@ -244,7 +244,7 @@ a { color: var(--color-primary); }
|
|
|
244
244
|
.fdy-avatar--tone-7{--_fdy-avatar-tone:var(--tone-7);}
|
|
245
245
|
.fdy-avatar--tone-8{--_fdy-avatar-tone:var(--tone-8);}
|
|
246
246
|
|
|
247
|
-
/* Freeday
|
|
247
|
+
/* Freeday. Badge */
|
|
248
248
|
.fdy-badge{display:inline-flex;align-items:center;gap:var(--space-1);font-family:var(--font-body);font-size:var(--text-xs);font-weight:var(--weight-semibold);line-height:1;white-space:nowrap;padding:.35em .7em;border-radius:var(--radius-full);color:var(--color-primary-strong);background:var(--color-primary-soft);border:var(--bw) solid color-mix(in srgb,var(--color-primary) 22%,transparent);box-shadow:0 1px 1.5px rgba(16,14,30,.05);}
|
|
249
249
|
.fdy-badge--success{color:var(--color-success-strong);background:var(--color-success-soft);border-color:color-mix(in srgb,var(--color-success) 26%,transparent);}
|
|
250
250
|
.fdy-badge--warning{color:var(--color-warning-strong);background:var(--color-warning-soft);border-color:color-mix(in srgb,var(--color-warning) 26%,transparent);}
|
|
@@ -252,12 +252,12 @@ a { color: var(--color-primary); }
|
|
|
252
252
|
.fdy-badge--info{color:var(--color-info-strong);background:var(--color-info-soft);border-color:color-mix(in srgb,var(--color-info) 26%,transparent);}
|
|
253
253
|
.fdy-badge--outline{background:transparent;border-color:var(--color-border-strong);color:var(--color-text-muted);box-shadow:none;}
|
|
254
254
|
|
|
255
|
-
/* Categorical tone badges
|
|
255
|
+
/* Categorical tone badges, a status vocabulary larger than the semantic palette, from the
|
|
256
256
|
* general --tone-1..8 tokens. Same validated AA formula as .fdy-avatar--tone-* and
|
|
257
257
|
* .fdy-chip--tone-* (text >=4.5:1 both themes, gated by test/contrast.test.mjs).
|
|
258
258
|
*
|
|
259
259
|
* Semantics come first: a state that IS good, bad or waiting takes --success / --danger /
|
|
260
|
-
* --warning. These are for the rest
|
|
260
|
+
* --warning. These are for the rest, the states a workflow distinguishes but a palette of five
|
|
261
261
|
* cannot. A back-office document list carrying Draft, Submitted, Approved, Completed, Settled,
|
|
262
262
|
* Closed, Transferred, InDeclaration, Open and Rejected in ONE column collapsed to three colours,
|
|
263
263
|
* which is the report this shipped for. */
|
|
@@ -271,7 +271,7 @@ a { color: var(--color-primary); }
|
|
|
271
271
|
.fdy-badge--tone-7{--_fdy-badge-tone:var(--tone-7);}
|
|
272
272
|
.fdy-badge--tone-8{--_fdy-badge-tone:var(--tone-8);}
|
|
273
273
|
|
|
274
|
-
/* Overlay badge
|
|
274
|
+
/* Overlay badge, anchors a small count/dot/icon badge to a corner of any element
|
|
275
275
|
* (icon, button, text). Wrap the content in .fdy-badge-wrap; add .fdy-badge-ov as a
|
|
276
276
|
* sibling. Default is a solid neutral pill; colour + dot + position via modifiers. */
|
|
277
277
|
.fdy-badge-wrap{position:relative;display:inline-flex;vertical-align:middle;}
|
|
@@ -281,7 +281,7 @@ a { color: var(--color-primary); }
|
|
|
281
281
|
/* Solid colours safe for count/text (on-* tokens, AA in light & dark) */
|
|
282
282
|
.fdy-badge-ov--primary{background:var(--color-primary);color:var(--color-on-primary);}
|
|
283
283
|
.fdy-badge-ov--danger{background:var(--color-danger-btn);color:var(--color-on-danger);}
|
|
284
|
-
/* Dot-oriented accents (for dots
|
|
284
|
+
/* Dot-oriented accents (for dots, plain text on these is not guaranteed AA) */
|
|
285
285
|
.fdy-badge-ov--accent{background:var(--color-accent);}
|
|
286
286
|
.fdy-badge-ov--info{background:var(--color-info);}
|
|
287
287
|
.fdy-badge-ov--success{background:var(--color-success);}
|
|
@@ -291,7 +291,7 @@ a { color: var(--color-primary); }
|
|
|
291
291
|
.fdy-badge-ov--bottom-right{top:auto;bottom:0;transform:translate(50%,50%);}
|
|
292
292
|
.fdy-badge-ov--bottom-left{top:auto;bottom:0;right:auto;left:0;transform:translate(-50%,50%);}
|
|
293
293
|
|
|
294
|
-
/* Freeday
|
|
294
|
+
/* Freeday. Breadcrumb */
|
|
295
295
|
.fdy-breadcrumb{font-size:var(--text-sm);}
|
|
296
296
|
.fdy-breadcrumb__list{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);list-style:none;margin:0;padding:0;}
|
|
297
297
|
.fdy-breadcrumb__item{display:inline-flex;align-items:center;gap:var(--space-2);color:var(--color-text-muted);}
|
|
@@ -301,23 +301,23 @@ a { color: var(--color-primary); }
|
|
|
301
301
|
.fdy-breadcrumb__item[aria-current="page"]{color:var(--color-text);font-weight:var(--weight-medium);}
|
|
302
302
|
.fdy-breadcrumb__sep{color:var(--color-text-subtle);user-select:none;}
|
|
303
303
|
|
|
304
|
-
/* Freeday
|
|
304
|
+
/* Freeday. Responsive display utilities (pairs with freeday-breakpoint.js).
|
|
305
305
|
* Breakpoint scale (min-width, px): sm 600 · md 960 · lg 1280 · xl 1920.
|
|
306
|
-
* NOTE: media-query breakpoints must be literal
|
|
306
|
+
* NOTE: media-query breakpoints must be literal. CSS can't read custom properties
|
|
307
307
|
* inside @media, so these px are structural, not stray magic numbers. */
|
|
308
308
|
.fdy-hidden{display:none !important;}
|
|
309
309
|
|
|
310
|
-
/* hide-below-X
|
|
310
|
+
/* hide-below-X, hidden when the viewport is narrower than X (content shows at X and up) */
|
|
311
311
|
@media (max-width:599.98px){.fdy-hide-below-sm{display:none !important;}}
|
|
312
312
|
@media (max-width:959.98px){.fdy-hide-below-md{display:none !important;}}
|
|
313
313
|
@media (max-width:1279.98px){.fdy-hide-below-lg{display:none !important;}}
|
|
314
314
|
|
|
315
|
-
/* hide-above-X
|
|
315
|
+
/* hide-above-X, hidden when the viewport is X and wider (content shows below X) */
|
|
316
316
|
@media (min-width:600px){.fdy-hide-above-sm{display:none !important;}}
|
|
317
317
|
@media (min-width:960px){.fdy-hide-above-md{display:none !important;}}
|
|
318
318
|
@media (min-width:1280px){.fdy-hide-above-lg{display:none !important;}}
|
|
319
319
|
|
|
320
|
-
/* Freeday
|
|
320
|
+
/* Freeday. Button */
|
|
321
321
|
.fdy-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);
|
|
322
322
|
height:var(--control-h);padding:0 var(--space-4);
|
|
323
323
|
font-family:var(--font-body);font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
|
@@ -330,7 +330,7 @@ a { color: var(--color-primary); }
|
|
|
330
330
|
.fdy-btn:active{transform:translateY(1px);box-shadow:0 1px 4px -1px color-mix(in srgb,var(--color-primary) 46%,transparent);}
|
|
331
331
|
.fdy-btn:disabled{opacity:.5;cursor:not-allowed;transform:none;box-shadow:none;}
|
|
332
332
|
/* Pressed / toggle. `aria-pressed` is already the right attribute for a toggle button, so this adds
|
|
333
|
-
no class and no new markup contract
|
|
333
|
+
no class and no new markup contract, and it is what makes .fdy-btn-group a complete segmented
|
|
334
334
|
control: the group joins the borders, this says which segment you are on. Each fill variant
|
|
335
335
|
defines its OWN pressed look below; a single shared rule cannot work, because a gradient is a
|
|
336
336
|
background-IMAGE and would blank the ghost variant's background-color. Solid: invert the gradient
|
|
@@ -342,12 +342,12 @@ a { color: var(--color-primary); }
|
|
|
342
342
|
.fdy-btn--ghost:hover{transform:none;background:var(--color-primary-soft);border-color:var(--color-primary-border);box-shadow:var(--shadow-1);}
|
|
343
343
|
.fdy-btn--ghost:active{transform:translateY(1px);}
|
|
344
344
|
|
|
345
|
-
/* --danger used to be the SOLID treatment and nothing else, so `fdy-btn--ghost fdy-btn--danger
|
|
346
|
-
which every reader parses as "quiet destructive"
|
|
345
|
+
/* --danger used to be the SOLID treatment and nothing else, so `fdy-btn--ghost fdy-btn--danger`,
|
|
346
|
+
which every reader parses as "quiet destructive", rendered a second SOLID button beside Save.
|
|
347
347
|
That is a hierarchy defect, not a cosmetic one: the base class is already the one primary per
|
|
348
348
|
screen. `.fdy-menu__item--danger` has always modelled the quiet form (red text, not a red block);
|
|
349
349
|
the pairing rules below bring it to the button. They are more specific than this one and come
|
|
350
|
-
after it, so this rule needs no :not() guard of its own
|
|
350
|
+
after it, so this rule needs no :not() guard of its own, one mechanism, not two. */
|
|
351
351
|
.fdy-btn--danger{background:linear-gradient(180deg,var(--color-danger-btn),color-mix(in srgb,#000 12%,var(--color-danger-btn)));color:var(--color-on-danger);box-shadow:0 2px 9px -1px color-mix(in srgb,var(--color-danger-btn) 46%,transparent),inset 0 1px 0 rgba(255,255,255,.22);}
|
|
352
352
|
.fdy-btn--danger:hover{transform:translateY(-1px);box-shadow:0 6px 18px -2px color-mix(in srgb,var(--color-danger-btn) 54%,transparent),inset 0 1px 0 rgba(255,255,255,.22);}
|
|
353
353
|
.fdy-btn--danger:active{transform:translateY(1px);}
|
|
@@ -361,21 +361,21 @@ a { color: var(--color-primary); }
|
|
|
361
361
|
.fdy-btn--sm{height:calc(var(--control-h) - var(--space-2));padding:0 var(--space-3);font-size:var(--text-xs);border-radius:var(--radius-sm);}
|
|
362
362
|
.fdy-btn--lg{height:calc(var(--control-h) + var(--space-2));padding:0 var(--space-6);font-size:var(--text-base);border-radius:var(--radius-lg);}
|
|
363
363
|
|
|
364
|
-
/* Icon inside a button
|
|
364
|
+
/* Icon inside a button, scales with the button's font size (works for text+icon & icon-only) */
|
|
365
365
|
.fdy-btn__icon{display:inline-flex;flex:none;}
|
|
366
366
|
.fdy-btn__icon svg{display:block;width:1.15em;height:1.15em;}
|
|
367
367
|
|
|
368
|
-
/* Text button
|
|
368
|
+
/* Text button, lowest emphasis (no fill/border), colored label, subtle hover tint */
|
|
369
369
|
.fdy-btn--text{background:none;color:var(--color-primary-strong);border-color:transparent;box-shadow:none;}
|
|
370
370
|
.fdy-btn--text:hover{transform:none;background:var(--color-primary-soft);box-shadow:none;}
|
|
371
371
|
.fdy-btn--text:active{transform:translateY(1px);box-shadow:none;}
|
|
372
|
-
/* Ghost/text pressed: the kit's selected-surface pair (soft fill + strong ink)
|
|
372
|
+
/* Ghost/text pressed: the kit's selected-surface pair (soft fill + strong ink), the same pairing
|
|
373
373
|
.fdy-nav__item uses for the current page, and one the AA contrast gate already covers. This is
|
|
374
374
|
the realistic segmented control: a row of --ghost buttons with exactly one aria-pressed. */
|
|
375
375
|
.fdy-btn--ghost[aria-pressed="true"],.fdy-btn--text[aria-pressed="true"]{background:var(--color-primary-soft);color:var(--color-primary-strong);border-color:var(--color-primary-border);box-shadow:none;}
|
|
376
376
|
.fdy-btn--ghost[aria-pressed="true"]:hover,.fdy-btn--text[aria-pressed="true"]:hover{background:var(--color-primary-soft);transform:none;box-shadow:none;}
|
|
377
377
|
|
|
378
|
-
/* Stretched hit area (#007)
|
|
378
|
+
/* Stretched hit area (#007), spread this button's click target over its nearest positioned
|
|
379
379
|
* ancestor, so "one card, one primary action, one escape hatch" needs no click handler on the
|
|
380
380
|
* <article>: the real control stays a real <button>/<a> and keeps its keyboard semantics.
|
|
381
381
|
* `.fdy-card` is already position:relative, so it is the anchor.
|
|
@@ -384,19 +384,19 @@ a { color: var(--color-primary); }
|
|
|
384
384
|
* a transformed element becomes the containing block for its own absolutely-positioned descendants,
|
|
385
385
|
* so `inset:0` re-anchors from the card to the button's own box the moment the button is hovered or
|
|
386
386
|
* pressed. Measured: mousedown lands on the button, the overlay collapses under the pointer, mouseup
|
|
387
|
-
* lands on some other element, and the browser fires `click` on their common ancestor
|
|
387
|
+
* lands on some other element, and the browser fires `click` on their common ancestor, the button
|
|
388
388
|
* never gets one. It demos fine on hover for --text/--ghost (their hover is already transform:none)
|
|
389
389
|
* and only a real press reveals it; the default and --danger fills break one step earlier, on hover.
|
|
390
390
|
* Press feedback moves to the card, which is the object actually being pressed (see card.css). */
|
|
391
391
|
.fdy-btn--stretch::after{content:'';position:absolute;inset:0;}
|
|
392
392
|
.fdy-btn--stretch:hover,.fdy-btn--stretch:active{transform:none;}
|
|
393
393
|
|
|
394
|
-
/* Icon-only
|
|
394
|
+
/* Icon-only, square; combine with --ghost/--text/--sm/--lg. Requires aria-label. */
|
|
395
395
|
.fdy-btn--icon{width:var(--control-h);padding:0;}
|
|
396
396
|
.fdy-btn--icon.fdy-btn--sm{width:calc(var(--control-h) - var(--space-2));}
|
|
397
397
|
.fdy-btn--icon.fdy-btn--lg{width:calc(var(--control-h) + var(--space-2));}
|
|
398
398
|
|
|
399
|
-
/* Button group
|
|
399
|
+
/* Button group, segmented, joined borders, rounded only at the ends */
|
|
400
400
|
.fdy-btn-group{display:inline-flex;}
|
|
401
401
|
.fdy-btn-group .fdy-btn{border-radius:0;}
|
|
402
402
|
.fdy-btn-group .fdy-btn:not(:first-child){margin-left:calc(var(--bw) * -1);}
|
|
@@ -404,7 +404,7 @@ a { color: var(--color-primary); }
|
|
|
404
404
|
.fdy-btn-group .fdy-btn:first-child{border-top-left-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md);}
|
|
405
405
|
.fdy-btn-group .fdy-btn:last-child{border-top-right-radius:var(--radius-md);border-bottom-right-radius:var(--radius-md);}
|
|
406
406
|
|
|
407
|
-
/* FAB
|
|
407
|
+
/* FAB, circular elevated action button (icon), optional extended pill with label */
|
|
408
408
|
.fdy-fab{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);width:3.5rem;height:3.5rem;padding:0;border:var(--bw) solid transparent;border-radius:var(--radius-full);cursor:pointer;color:var(--color-on-primary);background:linear-gradient(180deg,var(--color-primary),color-mix(in srgb,#000 10%,var(--color-primary)));box-shadow:var(--shadow-3);transition:transform var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
|
|
409
409
|
.fdy-fab:hover{transform:translateY(-1px);box-shadow:var(--shadow-4);}
|
|
410
410
|
.fdy-fab:active{transform:translateY(1px);}
|
|
@@ -417,35 +417,35 @@ a { color: var(--color-primary); }
|
|
|
417
417
|
.fdy-fab--danger{background:linear-gradient(180deg,var(--color-danger-btn),color-mix(in srgb,#000 12%,var(--color-danger-btn)));color:var(--color-on-danger);}
|
|
418
418
|
.fdy-fab--accent{background:linear-gradient(180deg,var(--color-accent),color-mix(in srgb,#000 10%,var(--color-accent)));color:var(--color-on-accent);}
|
|
419
419
|
|
|
420
|
-
/* Freeday
|
|
421
|
-
/* position:relative
|
|
420
|
+
/* Freeday. Card */
|
|
421
|
+
/* position:relative, containing block for out-of-flow card content (see base.css); it also gives
|
|
422
422
|
consumers the anchor a corner ribbon/badge needs. */
|
|
423
423
|
.fdy-card{position:relative;background:var(--color-surface);border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow-lift);transition:box-shadow var(--dur-base) var(--ease-standard),transform var(--dur-base) var(--ease-standard);}
|
|
424
424
|
.fdy-card--elevated{box-shadow:var(--shadow-lift-hover);}
|
|
425
425
|
.fdy-card--interactive{cursor:pointer;}
|
|
426
426
|
.fdy-card--interactive:hover{box-shadow:var(--shadow-lift-hover);transform:translateY(-3px);}
|
|
427
|
-
/* Pressing a stretched target (see .fdy-btn--stretch) is pressing the CARD
|
|
427
|
+
/* Pressing a stretched target (see .fdy-btn--stretch) is pressing the CARD, the button's own
|
|
428
428
|
nudge had to go, and the object under the pointer is the card. Ordered before the reduced-motion
|
|
429
429
|
block and the disabled rules so both still win at equal specificity. */
|
|
430
430
|
.fdy-card--interactive:has(.fdy-btn--stretch:active){transform:translateY(-1px);}
|
|
431
431
|
/* The global reduced-motion reset kills the ANIMATION; drop the -3px shift too so the card
|
|
432
432
|
* doesn't jump. The shadow still communicates the affordance. */
|
|
433
433
|
@media (prefers-reduced-motion:reduce){.fdy-card--interactive:hover,.fdy-card--interactive:has(.fdy-btn--stretch:active){transform:none;}}
|
|
434
|
-
/* When the whole card IS the control
|
|
435
|
-
* announced
|
|
434
|
+
/* When the whole card IS the control, a real <button> so the row is keyboard-reachable and
|
|
435
|
+
* announced, reset the UA button box. Note: it does NOT reset background/border, because
|
|
436
436
|
* .fdy-card already sets its own surface + border (resetting them here would strip the card). */
|
|
437
437
|
.fdy-card--button{display:block;width:100%;text-align:inherit;color:inherit;appearance:none;-webkit-appearance:none;}
|
|
438
|
-
/* Disabled card-as-control
|
|
438
|
+
/* Disabled card-as-control, same contract as .fdy-list__row--button: dim + not-allowed, and the
|
|
439
439
|
--interactive lift is withdrawn so a refusing control stops answering the pointer. */
|
|
440
440
|
.fdy-card--button:disabled,.fdy-card--button[aria-disabled="true"],.fdy-card--interactive[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
|
|
441
441
|
.fdy-card--interactive:disabled:hover,.fdy-card--interactive[aria-disabled="true"]:hover{box-shadow:var(--shadow-lift);transform:none;}
|
|
442
|
-
/* Escape hatches stay clickable above a stretched target
|
|
442
|
+
/* Escape hatches stay clickable above a stretched target, automatic, not a documented convention,
|
|
443
443
|
because forgetting it fails SILENTLY: the secondary control looks and hovers exactly as before and
|
|
444
444
|
simply never receives the click, which is the very symptom the pattern exists to cure. Matched by
|
|
445
445
|
shape (anything focusable) so a control the kit does not ship yet is covered too. */
|
|
446
446
|
:is(.fdy-card,.fdy-list__row):has(.fdy-btn--stretch) :is(a[href],button,input,select,textarea,[tabindex]):not(.fdy-btn--stretch){z-index:1;}
|
|
447
447
|
/* The position is a DEFAULT, not an override: z-index needs a positioned box (or a flex/grid item),
|
|
448
|
-
but an app that deliberately pins a control inside the card
|
|
448
|
+
but an app that deliberately pins a control inside the card, a corner dismiss, a favourite star,
|
|
449
449
|
must keep its own `position:absolute`. Wrapped in :where() so this whole selector weighs nothing
|
|
450
450
|
and ANY app rule wins it; measured, because at normal specificity the kit silently dragged an
|
|
451
451
|
absolutely positioned dismiss button back into the flow. Do not merge these two rules. */
|
|
@@ -455,9 +455,9 @@ a { color: var(--color-primary); }
|
|
|
455
455
|
.fdy-card__desc{font-size:var(--text-sm);color:var(--color-text-muted);margin:0;line-height:var(--leading-normal);}
|
|
456
456
|
.fdy-card__footer{padding:var(--space-3) var(--space-5);border-top:var(--bw) solid var(--color-border-muted);background:linear-gradient(180deg,var(--color-surface),var(--color-surface-2));display:flex;gap:var(--space-2);justify-content:flex-end;}
|
|
457
457
|
|
|
458
|
-
/* Freeday
|
|
458
|
+
/* Freeday. Carousel (scroll-snap track + arrows + dots). Enhanced by freeday-carousel.js. */
|
|
459
459
|
.fdy-carousel{position:relative;}
|
|
460
|
-
/* position:relative
|
|
460
|
+
/* position:relative means the arrows are positioned against .fdy-carousel (the parent), so this only
|
|
461
461
|
catches out-of-flow content inside the SLIDES, which would otherwise escape the viewport
|
|
462
462
|
entirely and scroll the page by one slide-offset per slide (see base.css). */
|
|
463
463
|
.fdy-carousel__viewport{position:relative;display:flex;overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;border-radius:var(--radius-lg);scrollbar-width:none;-ms-overflow-style:none;}
|
|
@@ -476,10 +476,10 @@ a { color: var(--color-primary); }
|
|
|
476
476
|
.fdy-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 40%,transparent);}
|
|
477
477
|
@media (prefers-reduced-motion:reduce){.fdy-carousel__viewport{scroll-behavior:auto;}}
|
|
478
478
|
|
|
479
|
-
/* Freeday
|
|
479
|
+
/* Freeday. Cascade select (hierarchical drill-down picker).
|
|
480
480
|
* Built by freeday-cascade.js from a [data-fdy-cascade] wrapper whose hidden
|
|
481
481
|
* nested <ul> is the data model: a <li> with a child <ul> is a branch, one
|
|
482
|
-
* without is a selectable leaf. The popover shows one level at a time
|
|
482
|
+
* without is a selectable leaf. The popover shows one level at a time, branches
|
|
483
483
|
* drill in, a back control ascends, a leaf selects (value = full path).
|
|
484
484
|
* Trigger visual matches the combo / date / time control family. */
|
|
485
485
|
.fdy-cascade{position:relative;display:inline-block;width:100%;max-width:22rem;}
|
|
@@ -516,16 +516,16 @@ a { color: var(--color-primary); }
|
|
|
516
516
|
.fdy-cascade__opt-arrow{flex:none;width:.45rem;height:.45rem;border-right:2px solid var(--color-text-subtle);border-bottom:2px solid var(--color-text-subtle);transform:rotate(-45deg);}
|
|
517
517
|
@media (prefers-reduced-motion:no-preference){.fdy-cascade__panel:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
518
518
|
|
|
519
|
-
/* Freeday
|
|
519
|
+
/* Freeday. Choose-from-list dialog (searchable master-data picker).
|
|
520
520
|
* Built on .fdy-modal: header + search + scrollable results table + footer.
|
|
521
521
|
* Backs a choose-from-list field (readonly input + .fdy-input-group__btn magnifier). */
|
|
522
522
|
.fdy-modal--cfl{width:min(46rem,calc(100vw - var(--space-8)));}
|
|
523
523
|
.fdy-modal--cfl .fdy-modal__body{padding:0;display:flex;flex-direction:column;min-height:0;overflow:hidden;}
|
|
524
524
|
|
|
525
|
-
/* Search bar
|
|
525
|
+
/* Search bar, single leading-icon field; Enter/typing filters the results. */
|
|
526
526
|
.fdy-cfl__search{flex:none;padding:var(--space-4) var(--space-5);border-bottom:var(--bw) solid var(--color-border-muted);}
|
|
527
527
|
|
|
528
|
-
/* Results
|
|
528
|
+
/* Results, dense rows, sticky header, its own scroll region. */
|
|
529
529
|
.fdy-cfl__results{overflow:auto;max-height:min(24rem,50vh);}
|
|
530
530
|
.fdy-cfl__results .fdy-table th{position:sticky;top:0;z-index:1;}
|
|
531
531
|
.fdy-cfl__row{cursor:pointer;}
|
|
@@ -540,12 +540,12 @@ a { color: var(--color-primary); }
|
|
|
540
540
|
.fdy-cfl__empty{padding:var(--space-8) var(--space-5);text-align:center;color:var(--color-text-muted);font-size:var(--text-sm);margin:0;}
|
|
541
541
|
.fdy-cfl__empty[hidden]{display:none;}
|
|
542
542
|
|
|
543
|
-
/* Footer
|
|
543
|
+
/* Footer, selection count (multi) sits left, actions right. */
|
|
544
544
|
.fdy-modal--cfl .fdy-modal__footer{justify-content:space-between;}
|
|
545
545
|
.fdy-cfl__count{font-size:var(--text-sm);color:var(--color-text-muted);}
|
|
546
546
|
.fdy-cfl__actions{display:flex;gap:var(--space-2);}
|
|
547
547
|
|
|
548
|
-
/* Freeday
|
|
548
|
+
/* Freeday. Charts (zero-dependency CSS/SVG, rendered by freeday-chart.js).
|
|
549
549
|
* Colour comes from --fdy-chart (a token, set inline by the renderer from data-fdy-color). */
|
|
550
550
|
|
|
551
551
|
/* Sparkline / trend line (SVG) */
|
|
@@ -565,7 +565,7 @@ a { color: var(--color-primary); }
|
|
|
565
565
|
/* Hover emphasis: dim all bars while the chart is hovered, keep the hovered one lit. */
|
|
566
566
|
@media (hover:hover){.fdy-bars:hover .fdy-bars__bar{opacity:.4;}.fdy-bars__col:hover .fdy-bars__bar{opacity:1;}}
|
|
567
567
|
|
|
568
|
-
/* Interactive tooltip (shared by bar + donut)
|
|
568
|
+
/* Interactive tooltip (shared by bar + donut). Chart.js-style hover readout. */
|
|
569
569
|
.fdy-chart__tip{position:absolute;z-index:5;left:0;top:0;pointer-events:none;transform:translate(-50%,calc(-100% - 8px));background:var(--color-inverse-surface);color:var(--color-inverse-text);font-size:var(--text-xs);font-weight:var(--weight-medium);font-variant-numeric:tabular-nums;padding:var(--space-1) var(--space-2);border-radius:var(--radius-sm);white-space:nowrap;box-shadow:var(--shadow-2);opacity:0;transition:opacity var(--dur-fast) var(--ease-standard);}
|
|
570
570
|
.fdy-chart__tip.is-visible{opacity:1;}
|
|
571
571
|
|
|
@@ -586,7 +586,7 @@ a { color: var(--color-primary); }
|
|
|
586
586
|
/* Horizontal legend for cartesian charts (legend sits above the plot). */
|
|
587
587
|
.fdy-chart__legend--row{flex-direction:row;flex-wrap:wrap;gap:var(--space-2) var(--space-4);}
|
|
588
588
|
|
|
589
|
-
/* Cartesian charts (line / area / multi-series & stacked bar)
|
|
589
|
+
/* Cartesian charts (line / area / multi-series & stacked bar). SVG with themed axes.
|
|
590
590
|
* Axis colour comes from --chart-grid / --chart-tick (both theme-aware); series colours are
|
|
591
591
|
* set inline by the renderer from --chart-1..8, so the whole chart recolours on data-theme. */
|
|
592
592
|
.fdy-chart-xy{position:relative;display:flex;flex-direction:column;gap:var(--space-3);width:100%;--fdy-chart-tick-size:var(--text-xs);}
|
|
@@ -601,10 +601,10 @@ a { color: var(--color-primary); }
|
|
|
601
601
|
.fdy-chart-xy__bar{transition:opacity var(--dur-fast) var(--ease-standard);}
|
|
602
602
|
.fdy-chart-xy__band{fill:transparent;cursor:pointer;}
|
|
603
603
|
|
|
604
|
-
/* Freeday
|
|
604
|
+
/* Freeday. Chip (tag / removable filter) */
|
|
605
605
|
.fdy-chip{display:inline-flex;align-items:center;gap:var(--space-2);height:1.75rem;padding:0 var(--space-3);border-radius:var(--radius-full);font-size:var(--text-xs);font-weight:var(--weight-medium);color:var(--color-text);background:var(--color-surface-3);border:var(--bw) solid var(--color-border);}
|
|
606
606
|
.fdy-chip--primary{color:var(--color-primary-strong);background:var(--color-primary-soft);border-color:color-mix(in srgb,var(--color-primary) 22%,transparent);}
|
|
607
|
-
/* Categorical tone chips
|
|
607
|
+
/* Categorical tone chips, non-semantic category tags / legend swatches, from the general
|
|
608
608
|
* --tone-1..8 palette. Same validated AA formula as .fdy-avatar--tone-* (text >=4.5:1 both themes,
|
|
609
609
|
* gated by test/contrast.test.mjs). */
|
|
610
610
|
.fdy-chip--tone-1,.fdy-chip--tone-2,.fdy-chip--tone-3,.fdy-chip--tone-4,.fdy-chip--tone-5,.fdy-chip--tone-6,.fdy-chip--tone-7,.fdy-chip--tone-8{background:color-mix(in srgb,var(--_fdy-chip-tone) 18%,var(--color-surface));color:color-mix(in srgb,var(--_fdy-chip-tone) 50%,var(--color-text));border-color:color-mix(in srgb,var(--_fdy-chip-tone) 30%,var(--color-border));}
|
|
@@ -632,14 +632,14 @@ a { color: var(--color-primary); }
|
|
|
632
632
|
.fdy-chip__check svg{display:block;width:100%;height:100%;}
|
|
633
633
|
.fdy-chip--filter[aria-pressed="true"] .fdy-chip__check{display:inline-flex;}
|
|
634
634
|
|
|
635
|
-
/* Freeday
|
|
635
|
+
/* Freeday. Combobox (custom styled select, WAI-ARIA APG select-only) */
|
|
636
636
|
.fdy-combo{position:relative;display:block;max-width:22rem;}
|
|
637
637
|
.fdy-combo__button{display:inline-flex;align-items:center;justify-content:space-between;gap:var(--space-2);width:100%;height:var(--control-h);padding:0 var(--space-3);text-align:left;cursor:pointer;font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
|
|
638
638
|
.fdy-combo__button:hover{border-color:var(--color-text-muted);}
|
|
639
639
|
.fdy-combo__button:focus-visible{outline:none;border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
640
640
|
.fdy-combo__button.is-open{border-color:var(--color-primary);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
641
641
|
.fdy-combo__button:disabled,.fdy-combo__button[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
|
|
642
|
-
/* Read-only: stays focusable & shows its value at full contrast but can't open
|
|
642
|
+
/* Read-only: stays focusable & shows its value at full contrast but can't open, muted surface,
|
|
643
643
|
softened border, default cursor, no focus-ring escalation (matches input[readonly], #39). */
|
|
644
644
|
.fdy-combo__button[aria-readonly="true"]{background:var(--color-surface-2);border-color:var(--color-border);cursor:default;}
|
|
645
645
|
.fdy-combo__button[aria-readonly="true"]:hover{border-color:var(--color-border);}
|
|
@@ -663,7 +663,7 @@ a { color: var(--color-primary); }
|
|
|
663
663
|
.fdy-combo__option.is-highlighted[aria-selected="true"]{box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--color-primary) 55%,transparent);}
|
|
664
664
|
/* The tick is CSS, not content (#004 §2). Inside role="option" it joined the option's ACCESSIBLE
|
|
665
665
|
NAME, so the selected option announced "✓August" while every other announced "August": the state
|
|
666
|
-
was read twice
|
|
666
|
+
was read twice, once as aria-selected, once as decoration, and a test asking for an option by
|
|
667
667
|
its own label stopped matching the one that was selected. The `/ ""` is alt text: the glyph is
|
|
668
668
|
painted, the accessibility tree gets nothing. Same reasoning as .fdy-label--required (1.29.0).
|
|
669
669
|
The empty box still reserves its width, so labels stay aligned whether selected or not. */
|
|
@@ -672,11 +672,11 @@ a { color: var(--color-primary); }
|
|
|
672
672
|
@media (prefers-reduced-motion:no-preference){.fdy-combo__listbox:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
673
673
|
@keyframes fdy-combo-in{from{opacity:0;transform:translateY(-4px);}to{opacity:1;transform:none;}}
|
|
674
674
|
|
|
675
|
-
/* Freeday
|
|
675
|
+
/* Freeday. Page composition & type roles.
|
|
676
676
|
*
|
|
677
677
|
* The kit stops at the component; these primitives encode *how a page is assembled* so screens
|
|
678
678
|
* built by different people cohere. They carry the usage doctrine (see USAGE.md) in markup: a
|
|
679
|
-
* named type role gets used, a doc gets skimmed. Everything here is token-driven layout
|
|
679
|
+
* named type role gets used, a doc gets skimmed. Everything here is token-driven layout, no new
|
|
680
680
|
* colours, no new shadows. Pair with .fdy-app (the shell) and the components inside.
|
|
681
681
|
*/
|
|
682
682
|
|
|
@@ -685,7 +685,7 @@ a { color: var(--color-primary); }
|
|
|
685
685
|
* page title (the mistake that collapses three levels into one grey mush). */
|
|
686
686
|
/* --color-text-muted, not -subtle: an eyebrow is a small label naming the thing below it, i.e.
|
|
687
687
|
prose. -subtle is gated at 3.0 on purpose (placeholders, separators, decorative glyphs) and
|
|
688
|
-
measures 4.41 on the page ground
|
|
688
|
+
measures 4.41 on the page ground, axe reports it as a serious color-contrast failure. Same
|
|
689
689
|
argument, same ink, same 4.41 as .fdy-stat__label in 1.30.0. */
|
|
690
690
|
.fdy-eyebrow{font-family:var(--font-body);font-size:var(--text-xs);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--color-text-muted);margin:0;}
|
|
691
691
|
.fdy-title-page{font-family:var(--font-display);font-size:var(--text-3xl);font-weight:var(--weight-bold);letter-spacing:var(--tracking-tighter);line-height:var(--leading-tight);color:var(--color-text);margin:0;text-wrap:balance;}
|
|
@@ -719,19 +719,19 @@ a { color: var(--color-primary); }
|
|
|
719
719
|
.fdy-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:var(--space-5);}
|
|
720
720
|
.fdy-stat{display:flex;flex-direction:column;gap:var(--space-1);min-width:0;}
|
|
721
721
|
/* --color-text-muted, not -subtle: this is a LABEL, i.e. text, and -subtle is deliberately gated at
|
|
722
|
-
3.0 (placeholders, dividers, decorative glyphs). On surface-2 -subtle measures 4.41
|
|
722
|
+
3.0 (placeholders, dividers, decorative glyphs). On surface-2 -subtle measures 4.41, under AA
|
|
723
723
|
for text, and a stat label is often the only thing naming the number above it. */
|
|
724
724
|
.fdy-stat__label{font-size:var(--text-xs);font-weight:var(--weight-semibold);letter-spacing:var(--tracking-wide);text-transform:uppercase;color:var(--color-text-muted);}
|
|
725
725
|
.fdy-stat__value{font-family:var(--font-display);font-size:var(--text-3xl);font-weight:var(--weight-bold);letter-spacing:var(--tracking-tighter);line-height:1;color:var(--color-text);font-variant-numeric:tabular-nums;}
|
|
726
726
|
.fdy-stat__value small{font-size:var(--text-lg);font-weight:var(--weight-semibold);color:var(--color-text-muted);}
|
|
727
727
|
.fdy-stat__meta{font-size:var(--text-sm);color:var(--color-text-muted);display:flex;align-items:center;gap:var(--space-2);}
|
|
728
|
-
/* A stat value is display type sized for a COUNT
|
|
728
|
+
/* A stat value is display type sized for a COUNT, "1,284", and a back office puts money in it.
|
|
729
729
|
"IDR 300,000.00" needs 224px at --text-3xl and the grid's own track is 11rem, so it wrapped
|
|
730
730
|
between the currency and the number: two lines of 31px where the design says one. Measured, not
|
|
731
731
|
guessed (#020).
|
|
732
732
|
The value shrinks only when its own column is too narrow to hold it, so a wide dashboard keeps
|
|
733
733
|
exactly the type it has today. Scoped to a stat INSIDE .fdy-stats, where the width comes from the
|
|
734
|
-
grid track
|
|
734
|
+
grid track, `container-type:inline-size` on a standalone .fdy-stat would stop it sizing to its
|
|
735
735
|
own content. The unconditional rule above stays as the fallback wherever @container is missing. */
|
|
736
736
|
@supports (container-type:inline-size){
|
|
737
737
|
.fdy-stats>.fdy-stat{container-type:inline-size;}
|
|
@@ -744,7 +744,7 @@ a { color: var(--color-primary); }
|
|
|
744
744
|
/* Icon box (#001 §4). The kit ships no icons on purpose, but every icon slot it DOES ship
|
|
745
745
|
(.fdy-btn__icon, .fdy-nav__icon, .fdy-state__icon) assumes something sensible inside, so a
|
|
746
746
|
standalone icon had no contract and every app invented one. 1em keeps the glyph matched to the
|
|
747
|
-
text beside it at every scale
|
|
747
|
+
text beside it at every scale, bring your own paths, use our box. Works on the <svg> itself or
|
|
748
748
|
on a wrapper around it. */
|
|
749
749
|
.fdy-icon{display:inline-block;width:1em;height:1em;flex:none;vertical-align:-0.125em;}
|
|
750
750
|
.fdy-icon>svg{display:block;width:100%;height:100%;}
|
|
@@ -757,13 +757,13 @@ a { color: var(--color-primary); }
|
|
|
757
757
|
.fdy-text-warning{color:var(--color-warning-strong);}
|
|
758
758
|
.fdy-text-danger{color:var(--color-danger-strong);}
|
|
759
759
|
|
|
760
|
-
/* Freeday
|
|
760
|
+
/* Freeday. Date picker (input-styled trigger + calendar popover).
|
|
761
761
|
* Built by freeday-datepicker.js from an empty [data-fdy-datepicker] wrapper. */
|
|
762
762
|
.fdy-datepicker{position:relative;display:inline-block;width:100%;max-width:14rem;}
|
|
763
763
|
/* Inside a labelled field, a picker fills the field like every other control.
|
|
764
764
|
The 14rem/11rem caps above suit a picker standing on its own; in a form grid
|
|
765
|
-
they leave the date box visibly short of the combo beside it
|
|
766
|
-
319px in a two-column layout
|
|
765
|
+
they leave the date box visibly short of the combo beside it. 224px against
|
|
766
|
+
319px in a two-column layout, and the row reads as ragged. The field's own
|
|
767
767
|
max-width is what should govern there. */
|
|
768
768
|
.fdy-field>.fdy-datepicker,.fdy-field>.fdy-timepicker,.fdy-field>.fdy-daterange{max-width:none;}
|
|
769
769
|
.fdy-datepicker__trigger{display:inline-flex;align-items:center;gap:var(--space-2);width:100%;height:var(--control-h);padding:0 var(--space-3);text-align:left;cursor:pointer;font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
|
|
@@ -809,12 +809,12 @@ a { color: var(--color-primary); }
|
|
|
809
809
|
.fdy-cal__nav:hover{background:var(--color-surface-2);color:var(--color-text);}
|
|
810
810
|
.fdy-cal__nav:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
811
811
|
.fdy-cal__grid{display:grid;grid-template-columns:repeat(7,2rem);gap:2px;}
|
|
812
|
-
/* Month grid
|
|
812
|
+
/* Month grid. 3×4 over the same track width as the 7-column day grid, so the panel does not resize
|
|
813
813
|
when you drill and the pointer does not have to travel.
|
|
814
814
|
|
|
815
815
|
AFTER `.fdy-cal__grid`, and that is the whole rule: the element carries both classes, they weigh
|
|
816
816
|
the same, so source order decides. Declared earlier, the modifier lost to the base it modifies and
|
|
817
|
-
twelve months rendered seven across
|
|
817
|
+
twelve months rendered seven across, the layout the comment above described, never once applied. */
|
|
818
818
|
.fdy-cal__grid--months,.fdy-cal__grid--years{grid-template-columns:repeat(3,1fr);gap:var(--space-1);}
|
|
819
819
|
.fdy-cal__dow{display:flex;align-items:center;justify-content:center;height:1.75rem;font-size:var(--text-xs);font-weight:var(--weight-semibold);color:var(--color-text-muted);text-transform:capitalize;}
|
|
820
820
|
.fdy-cal__day{display:inline-flex;align-items:center;justify-content:center;width:2rem;height:2rem;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text);font-size:var(--text-sm);font-variant-numeric:tabular-nums;cursor:pointer;}
|
|
@@ -832,14 +832,14 @@ a { color: var(--color-primary); }
|
|
|
832
832
|
/* Responsive: each picker may shrink (min-width:0) & wrap so the range never overflows a
|
|
833
833
|
narrow row/phone; inline-flex still hugs to ~23rem at wider widths (look unchanged). */
|
|
834
834
|
.fdy-daterange{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);}
|
|
835
|
-
/* Each picker shrinks from an 11rem basis but not below ~7rem
|
|
835
|
+
/* Each picker shrinks from an 11rem basis but not below ~7rem, enough for "dd/mm/yyyy" + the icon,
|
|
836
836
|
so a squeezed range compresses gracefully (and wraps) instead of clipping the date. */
|
|
837
837
|
.fdy-daterange .fdy-datepicker{flex:0 1 11rem;min-width:7rem;}
|
|
838
838
|
.fdy-daterange__sep{flex:none;color:var(--color-text-subtle);}
|
|
839
839
|
|
|
840
840
|
@media (prefers-reduced-motion:no-preference){.fdy-datepicker__panel:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
841
841
|
|
|
842
|
-
/* Freeday
|
|
842
|
+
/* Freeday. Datetime picker (composed: date picker + time picker → one value).
|
|
843
843
|
* A thin composer (freeday-datetime.js) links a child [data-fdy-datepicker] and
|
|
844
844
|
* [data-fdy-timepicker] and emits a single combined event. Icon variants come from
|
|
845
845
|
* the child pickers (data-fdy-no-icon / <template data-fdy-icon>). */
|
|
@@ -848,18 +848,18 @@ a { color: var(--color-primary); }
|
|
|
848
848
|
.fdy-datetimepicker .fdy-datepicker{width:12rem;max-width:none;}
|
|
849
849
|
.fdy-datetimepicker .fdy-timepicker{width:8.5rem;max-width:none;}
|
|
850
850
|
|
|
851
|
-
/* Freeday
|
|
851
|
+
/* Freeday. Description list (key-value detail view) */
|
|
852
852
|
.fdy-dl{display:grid;grid-template-columns:auto 1fr;gap:var(--space-2) var(--space-5);margin:0;}
|
|
853
853
|
.fdy-dl dt{color:var(--color-text-muted);font-size:var(--text-sm);}
|
|
854
854
|
.fdy-dl dd{margin:0;color:var(--color-text);font-size:var(--text-sm);font-weight:var(--weight-medium);}
|
|
855
855
|
.fdy-dl--rows{grid-template-columns:1fr;gap:var(--space-1);}
|
|
856
856
|
.fdy-dl--rows dt{margin-top:var(--space-2);}
|
|
857
857
|
|
|
858
|
-
/* Freeday
|
|
858
|
+
/* Freeday. Divider */
|
|
859
859
|
.fdy-divider{border:0;border-top:var(--bw) solid var(--color-border);margin:var(--space-4) 0;}
|
|
860
860
|
.fdy-divider--vertical{border-top:0;border-left:var(--bw) solid var(--color-border);margin:0 var(--space-4);align-self:stretch;}
|
|
861
861
|
|
|
862
|
-
/* Freeday
|
|
862
|
+
/* Freeday. Drawer (temporary side panel on native <dialog>: focus-trap / Esc /
|
|
863
863
|
* backdrop / return-focus are free). Anchors left (default) or right.
|
|
864
864
|
* Enhanced by freeday-drawer.js (open trigger + backdrop-click close). */
|
|
865
865
|
/* display is gated behind [open] so the native <dialog> stays hidden until opened.
|
|
@@ -886,7 +886,7 @@ a { color: var(--color-primary); }
|
|
|
886
886
|
.fdy-drawer[open]::backdrop{opacity:0;}
|
|
887
887
|
}
|
|
888
888
|
|
|
889
|
-
/* Freeday
|
|
889
|
+
/* Freeday. File upload (drop target + explicit per-file states).
|
|
890
890
|
* Enhanced by freeday-upload.js; states: idle · drag-over · uploading · done · error. */
|
|
891
891
|
.fdy-dropzone{display:flex;flex-direction:column;align-items:center;gap:var(--space-2);padding:var(--space-6);text-align:center;border:2px dashed var(--color-control-border);border-radius:var(--radius-lg);background:var(--color-surface-2);color:var(--color-text-muted);cursor:pointer;transition:border-color var(--dur-fast) var(--ease-standard),background var(--dur-fast) var(--ease-standard),color var(--dur-fast) var(--ease-standard);}
|
|
892
892
|
.fdy-dropzone:hover{border-color:var(--color-text-muted);}
|
|
@@ -898,17 +898,17 @@ a { color: var(--color-primary); }
|
|
|
898
898
|
.fdy-dropzone__icon svg{display:block;width:2rem;height:2rem;}
|
|
899
899
|
.fdy-dropzone__title{font-size:inherit;font-weight:var(--weight-medium);color:var(--color-text);margin:0;}
|
|
900
900
|
.fdy-dropzone__hint{font-size:var(--text-xs);}
|
|
901
|
-
/* Wide (horizontal) layout
|
|
901
|
+
/* Wide (horizontal) layout, icon beside the copy instead of stacked, for full-width
|
|
902
902
|
* sections. Wrap the title + hint in .fdy-dropzone__text. */
|
|
903
903
|
.fdy-dropzone--row{flex-direction:row;align-items:center;justify-content:center;text-align:left;gap:var(--space-4);}
|
|
904
904
|
.fdy-dropzone__text{display:flex;flex-direction:column;gap:var(--space-1);}
|
|
905
905
|
|
|
906
906
|
/* File rows (list of added/uploaded/rejected files) */
|
|
907
|
-
/* Symmetric on purpose. The list carried margin-top and nothing below, so whatever follows it
|
|
908
|
-
an "Add files" button, in every upload UI there is
|
|
907
|
+
/* Symmetric on purpose. The list carried margin-top and nothing below, so whatever follows it,
|
|
908
|
+
an "Add files" button, in every upload UI there is, sat flush against the last row: measured
|
|
909
909
|
0px. A block that claims space on one side only is not spacing, it is a lean. */
|
|
910
910
|
.fdy-filelist{display:flex;flex-direction:column;gap:var(--space-2);margin-top:var(--space-3);margin-bottom:var(--space-3);}
|
|
911
|
-
/* Nothing after it, nothing to separate from
|
|
911
|
+
/* Nothing after it, nothing to separate from, so the room is not spent. */
|
|
912
912
|
.fdy-filelist:last-child{margin-bottom:0;}
|
|
913
913
|
/* Side-by-side rows on wide screens (reflow to one column when narrow). */
|
|
914
914
|
.fdy-filelist--grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(16rem,1fr));}
|
|
@@ -927,7 +927,7 @@ a { color: var(--color-primary); }
|
|
|
927
927
|
.fdy-file--error .fdy-file__icon{color:var(--color-danger);}
|
|
928
928
|
.fdy-file--error .fdy-file__sub{color:var(--color-danger-strong);}
|
|
929
929
|
|
|
930
|
-
/* Freeday
|
|
930
|
+
/* Freeday. Filter bar (a consistent row of filters: search + selects + actions).
|
|
931
931
|
* Solves the "ragged filter row": apps improvised flex rows with ad-hoc
|
|
932
932
|
* flex/min-width, so grow fields ballooned while selects stayed tiny and no two
|
|
933
933
|
* screens matched. Here every field snaps to a shared width rhythm; one field may
|
|
@@ -936,7 +936,7 @@ a { color: var(--color-primary); }
|
|
|
936
936
|
.fdy-filterbar{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--space-3) var(--space-4);}
|
|
937
937
|
/* Composed with a table toolbar (one element, both classes): the fields decide the alignment.
|
|
938
938
|
* `.fdy-table-toolbar` sets align-items:center (right for bare buttons) at equal specificity, and
|
|
939
|
-
* being later in the bundle it would otherwise win
|
|
939
|
+
* being later in the bundle it would otherwise win, floating labelled fields' neighbours out of line.
|
|
940
940
|
* This 2-class selector (0,2,0) settles it toward the filterbar's flex-end regardless of source order. */
|
|
941
941
|
.fdy-table-toolbar.fdy-filterbar{align-items:flex-end;}
|
|
942
942
|
/* Fields are direct children. Drop the standalone field's 22rem cap; default to the
|
|
@@ -944,7 +944,7 @@ a { color: var(--color-primary); }
|
|
|
944
944
|
.fdy-filterbar>.fdy-field{max-width:none;flex:0 0 auto;width:12rem;}
|
|
945
945
|
/* Controls fill their field's column (drop the standalone input-group/combo caps). */
|
|
946
946
|
.fdy-filterbar>.fdy-field .fdy-input-group,.fdy-filterbar>.fdy-field .fdy-combo{max-width:none;width:100%;}
|
|
947
|
-
/* Column-width rhythm
|
|
947
|
+
/* Column-width rhythm, pick one per field so bars across the app line up. */
|
|
948
948
|
.fdy-filterbar>.fdy-field--w-sm{width:9rem;}
|
|
949
949
|
.fdy-filterbar>.fdy-field--w-lg{width:15rem;}
|
|
950
950
|
.fdy-filterbar>.fdy-field--w-xl{width:18rem;}
|
|
@@ -954,11 +954,11 @@ a { color: var(--color-primary); }
|
|
|
954
954
|
.fdy-filterbar>.fdy-field--w-grow{flex:1 1 16rem;width:auto;min-width:12rem;}
|
|
955
955
|
/* Trailing actions (buttons) pushed to the end, aligned with the controls' baseline.
|
|
956
956
|
* Wrap note: a wrapped bar breaks flex lines on each item's *flex-basis* (the grow field's
|
|
957
|
-
* `flex:1 1 16rem` reserves 16rem for line-breaking), NOT its rendered width
|
|
957
|
+
* `flex:1 1 16rem` reserves 16rem for line-breaking), NOT its rendered width, so the grow basis,
|
|
958
958
|
* not the visible width, decides the wrap point. Margins can't keep the actions off a lonely wrapped
|
|
959
959
|
* row; to avoid stranded actions, tune the --w-grow basis or drop a field so the bar fits one row. */
|
|
960
|
-
/* A control that carries NO label of its own
|
|
961
|
-
button
|
|
960
|
+
/* A control that carries NO label of its own, a checkbox, a switch, a lone
|
|
961
|
+
button, in a bar whose `align-items:flex-end` exists to line up LABELLED
|
|
962
962
|
fields. Flex-end puts such a control's bottom on the inputs' bottom, so a
|
|
963
963
|
20px checkbox sits well below the 32px input's centre line and reads as
|
|
964
964
|
dropped. Giving it the height of a control lands its box exactly where an
|
|
@@ -966,7 +966,7 @@ a { color: var(--color-primary); }
|
|
|
966
966
|
line. Raised from IDU_EMATE_APPL_WEB (#014). */
|
|
967
967
|
.fdy-filterbar>.fdy-check,.fdy-filterbar>.fdy-radio,.fdy-filterbar>.fdy-switch,.fdy-filterbar>.fdy-btn{min-height:var(--control-h);}
|
|
968
968
|
.fdy-filterbar__actions{display:flex;align-items:center;gap:var(--space-2);margin-left:auto;}
|
|
969
|
-
/* Opt-in: sit the actions inline right after the last field instead of at the far edge
|
|
969
|
+
/* Opt-in: sit the actions inline right after the last field instead of at the far edge, so when the
|
|
970
970
|
* bar wraps they read as attached to the filters rather than shoved to the right of a lonely row. */
|
|
971
971
|
.fdy-filterbar--actions-inline .fdy-filterbar__actions{margin-left:0;}
|
|
972
972
|
/* Stack on narrow screens so nothing overflows the row. */
|
|
@@ -975,16 +975,16 @@ a { color: var(--color-primary); }
|
|
|
975
975
|
.fdy-filterbar__actions{width:100%;margin-left:0;}
|
|
976
976
|
}
|
|
977
977
|
|
|
978
|
-
/* Freeday
|
|
978
|
+
/* Freeday. Form grid (document/header fields laid out in aligned columns).
|
|
979
979
|
* The two-dimensional counterpart to the single-column .fdy-field stack and the
|
|
980
980
|
* one-row .fdy-filterbar: related fields sit in columns that reflow to one on narrow
|
|
981
981
|
* screens. Fields are the standard .fdy-field; a field with --full spans the whole row
|
|
982
|
-
* (e.g. an address or note). Zero JS
|
|
982
|
+
* (e.g. an address or note). Zero JS, pure layout over .fdy-field. */
|
|
983
983
|
.fdy-form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));gap:var(--space-4) var(--space-5);}
|
|
984
984
|
.fdy-form-grid>.fdy-field{max-width:none;}
|
|
985
985
|
.fdy-form-grid>.fdy-field--full{grid-column:1/-1;}
|
|
986
986
|
|
|
987
|
-
/* Freeday
|
|
987
|
+
/* Freeday. Input group (prefix/suffix addons, e.g. Rp / % / icon) */
|
|
988
988
|
.fdy-input-group{display:flex;align-items:stretch;height:var(--control-h);max-width:22rem;background:var(--color-surface);border:1.5px solid var(--color-control-border);border-radius:var(--radius-md);box-shadow:inset 0 1px 2px rgba(16,14,30,.06);overflow:hidden;transition:border-color var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
|
|
989
989
|
.fdy-input-group:hover{border-color:var(--color-text-muted);}
|
|
990
990
|
.fdy-input-group:focus-within{border-color:var(--color-primary);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
@@ -997,34 +997,34 @@ a { color: var(--color-primary); }
|
|
|
997
997
|
.fdy-input-group .fdy-input:focus{box-shadow:none;}
|
|
998
998
|
.fdy-input-group__addon{display:inline-flex;align-items:center;padding:0 var(--space-3);background:var(--color-surface-2);color:var(--color-text-muted);font-size:var(--text-sm);white-space:nowrap;border-right:var(--bw) solid var(--color-border);}
|
|
999
999
|
.fdy-input-group__addon:last-child{border-right:0;border-left:var(--bw) solid var(--color-border);}
|
|
1000
|
-
/* Borderless leading icon (e.g. search magnifier)
|
|
1000
|
+
/* Borderless leading icon (e.g. search magnifier), self-contained like __btn so it
|
|
1001
1001
|
centers standalone (no base .fdy-input-group__addon required). */
|
|
1002
1002
|
.fdy-input-group__addon--icon{display:inline-flex;align-items:center;background:transparent;border:0;padding:0 0 0 var(--space-3);color:var(--color-text-subtle);}
|
|
1003
1003
|
.fdy-input-group__addon--icon svg{display:block;width:1rem;height:1rem;}
|
|
1004
|
-
/* The leading icon already provides the left inset
|
|
1004
|
+
/* The leading icon already provides the left inset, don't let the input add its full left padding on top. */
|
|
1005
1005
|
.fdy-input-group__addon--icon + .fdy-input{padding-left:var(--space-2);}
|
|
1006
|
-
/* Trailing action button (e.g. choose-from-list magnifier)
|
|
1006
|
+
/* Trailing action button (e.g. choose-from-list magnifier), a real, focusable control. */
|
|
1007
1007
|
.fdy-input-group__btn{display:inline-flex;align-items:center;justify-content:center;flex:none;width:var(--control-h);align-self:stretch;border:0;border-left:var(--bw) solid var(--color-border);background:var(--color-surface-2);color:var(--color-text-muted);cursor:pointer;transition:background var(--dur-fast) var(--ease-standard),color var(--dur-fast) var(--ease-standard);}
|
|
1008
1008
|
.fdy-input-group__btn:hover{background:var(--color-surface-3);color:var(--color-text);}
|
|
1009
1009
|
.fdy-input-group__btn:focus-visible{outline:none;color:var(--color-text);box-shadow:inset 0 0 0 2px var(--color-primary);}
|
|
1010
1010
|
.fdy-input-group__btn svg{display:block;width:1.1rem;height:1.1rem;}
|
|
1011
1011
|
/* Leading action button (the "−" of a number stepper): the divider belongs on the other side. */
|
|
1012
1012
|
.fdy-input-group__btn:first-child{border-left:0;border-right:var(--bw) solid var(--color-border);}
|
|
1013
|
-
/* At a bound, or on a disabled/read-only field, the button must look as inert as it acts
|
|
1013
|
+
/* At a bound, or on a disabled/read-only field, the button must look as inert as it acts, same dim
|
|
1014
1014
|
+ not-allowed as .fdy-input:disabled. :hover still matches a disabled button, so it is withdrawn. */
|
|
1015
1015
|
.fdy-input-group__btn:disabled{opacity:.5;cursor:not-allowed;}
|
|
1016
1016
|
.fdy-input-group__btn:disabled:hover{background:var(--color-surface-2);color:var(--color-text-muted);}
|
|
1017
|
-
/* Error state must win over the neutralizing rules above
|
|
1017
|
+
/* Error state must win over the neutralizing rules above, promote to the wrapper. */
|
|
1018
1018
|
.fdy-input-group:has(.fdy-input--error),.fdy-input-group:has(.fdy-combo--error),.fdy-input-group:has([aria-invalid="true"]){border-color:var(--color-danger);}
|
|
1019
1019
|
.fdy-input-group:has(.fdy-input--error):focus-within,.fdy-input-group:has(.fdy-combo--error):focus-within,.fdy-input-group:has([aria-invalid="true"]):focus-within{border-color:var(--color-danger);box-shadow:inset 0 1px 2px rgba(16,14,30,.04),0 0 0 3px color-mix(in srgb,var(--color-danger) 26%,transparent);}
|
|
1020
1020
|
|
|
1021
|
-
/* Freeday
|
|
1021
|
+
/* Freeday. Field & Input */
|
|
1022
1022
|
.fdy-field{display:flex;flex-direction:column;gap:var(--space-2);max-width:22rem;}
|
|
1023
|
-
/* A grouped field is legitimately a <fieldset> (it needs a <legend>)
|
|
1023
|
+
/* A grouped field is legitimately a <fieldset> (it needs a <legend>), e.g. a radio/checkbox group.
|
|
1024
1024
|
Neutralise the UA groove border / padding / min-inline-size so it lays out identically to the
|
|
1025
1025
|
<div> form (min-inline-size:min-content would otherwise block the filterbar column widths). */
|
|
1026
1026
|
fieldset.fdy-field{border:0;margin:0;padding:0;min-inline-size:0;}
|
|
1027
|
-
/* The rendered legend is laid out outside the fieldset's flex flow, so `gap` never reaches it
|
|
1027
|
+
/* The rendered legend is laid out outside the fieldset's flex flow, so `gap` never reaches it,
|
|
1028
1028
|
measured 0px between legend and first control. This is the spacing contract a grouped field
|
|
1029
1029
|
needs; it matches .fdy-field's own gap so the group reads as one rhythm. */
|
|
1030
1030
|
fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
@@ -1042,40 +1042,40 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1042
1042
|
.fdy-input[readonly]:hover,.fdy-textarea[readonly]:hover{border-color:var(--color-border);}
|
|
1043
1043
|
.fdy-input[readonly]:focus-visible,.fdy-textarea[readonly]:focus-visible{border-color:var(--color-border-strong);box-shadow:none;}
|
|
1044
1044
|
/* Numbers (#44): the user agent draws its own spin buttons inside the field, in OS widget colours no
|
|
1045
|
-
theme reaches
|
|
1045
|
+
theme reaches, on a dark surface they read as a light-grey artefact glued to a themed field, and
|
|
1046
1046
|
it is the only unthemed control on the page. Removed so .fdy-input looks the same whatever its
|
|
1047
1047
|
type. `appearance:textfield` is the Firefox half, the pseudo-element the Blink/WebKit half; both
|
|
1048
|
-
are needed. This trades away the mouse-only increment
|
|
1048
|
+
are needed. This trades away the mouse-only increment, arrow keys on a focused number input still
|
|
1049
1049
|
work, and [data-fdy-number] gives the affordance back on the kit's terms. */
|
|
1050
1050
|
.fdy-input[type="number"]{appearance:textfield;-webkit-appearance:textfield;}
|
|
1051
1051
|
.fdy-input[type="number"]::-webkit-outer-spin-button,.fdy-input[type="number"]::-webkit-inner-spin-button{appearance:none;-webkit-appearance:none;margin:0;}
|
|
1052
1052
|
.fdy-input--error,.fdy-input[aria-invalid="true"],.fdy-textarea[aria-invalid="true"]{border-color:var(--color-danger);}
|
|
1053
1053
|
.fdy-input--error:focus,.fdy-input[aria-invalid="true"]:focus,.fdy-textarea[aria-invalid="true"]:focus{border-color:var(--color-danger);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-danger) 26%,transparent);}
|
|
1054
1054
|
/* Required marker. The control already carries `required`, so the asterisk is decoration: the
|
|
1055
|
-
`/ ""` is CSS alt text
|
|
1055
|
+
`/ ""` is CSS alt text, the glyph is painted but exposes NOTHING to the accessibility tree, so a
|
|
1056
1056
|
screen reader does not read "star" after every label. That is stricter than a <span> an app has to
|
|
1057
1057
|
remember to mark aria-hidden, and it cannot be forgotten. */
|
|
1058
1058
|
.fdy-label--required::after{content:"*" / "";margin-left:var(--space-1);color:var(--color-danger);}
|
|
1059
1059
|
.fdy-help{font-size:var(--text-xs);color:var(--color-text-muted);}
|
|
1060
1060
|
.fdy-help--error{color:var(--color-danger);}
|
|
1061
1061
|
|
|
1062
|
-
/* Freeday
|
|
1062
|
+
/* Freeday. Keyboard key */
|
|
1063
1063
|
.fdy-kbd{display:inline-flex;align-items:center;justify-content:center;min-width:1.5rem;height:1.5rem;padding:0 var(--space-2);font-family:var(--font-mono);font-size:var(--text-xs);color:var(--color-text);background:var(--color-surface-2);border:var(--bw) solid var(--color-border-strong);border-bottom-width:2px;border-radius:var(--radius-sm);}
|
|
1064
1064
|
|
|
1065
|
-
/* Freeday
|
|
1065
|
+
/* Freeday. List: the FLAT row container.
|
|
1066
1066
|
*
|
|
1067
1067
|
* USAGE.md §3 puts list rows among the surfaces that should be flat, but until now the only
|
|
1068
|
-
* container the kit shipped was .fdy-card
|
|
1068
|
+
* container the kit shipped was .fdy-card, which carries --shadow-lift (a real 34px lift). A
|
|
1069
1069
|
* responsive table that becomes a list below `md` therefore had to choose between a stack of
|
|
1070
1070
|
* shadowed cards (against the doctrine) or a hand-built box, which needs a colour and so escapes
|
|
1071
1071
|
* the token system. This is that missing container: one flat surface, hairline dividers, no shadow.
|
|
1072
1072
|
*
|
|
1073
1073
|
* Not to be confused with .fdy-list-reset (base.css), which only strips UA bullets/indent.
|
|
1074
1074
|
* Works on <ul>/<ol> (list-style is reset here) or on plain <div>s. */
|
|
1075
|
-
/* position:relative
|
|
1075
|
+
/* position:relative, containing block for out-of-flow row content (see base.css). */
|
|
1076
1076
|
.fdy-list{position:relative;list-style:none;margin:0;padding:0;background:var(--color-surface);border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;}
|
|
1077
1077
|
/* A row that hosts a stretched target has to be the anchor itself (#008 §1). Without this the
|
|
1078
|
-
overlay resolves against .fdy-list
|
|
1078
|
+
overlay resolves against .fdy-list, which IS positioned, so EVERY row's target covers the whole
|
|
1079
1079
|
list and the last one in the DOM wins: measured, a click on row one opens row two, under row one's
|
|
1080
1080
|
name, which looks exactly like working software. Opt-in via :has(), so a row that doesn't use the
|
|
1081
1081
|
pattern is untouched and nothing an app absolutely positions inside a plain row starts resolving
|
|
@@ -1084,7 +1084,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1084
1084
|
.fdy-list__row{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);min-width:0;}
|
|
1085
1085
|
/* Divider between rows. Two shapes are supported and both are load-bearing: rows as direct children
|
|
1086
1086
|
* of the list, and rows wrapped in <li> (the semantic shape, where the ADJACENT siblings are the
|
|
1087
|
-
* <li>s
|
|
1087
|
+
* <li>s, a bare `.fdy-list__row + .fdy-list__row` silently matches nothing there). */
|
|
1088
1088
|
.fdy-list__row + .fdy-list__row,
|
|
1089
1089
|
.fdy-list > * + * > .fdy-list__row{border-top:var(--bw) solid var(--color-border-muted);}
|
|
1090
1090
|
/* A row that is itself the control: render it as <button>/<a> and the UA box is reset without
|
|
@@ -1093,7 +1093,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1093
1093
|
.fdy-list__row--button:hover,.fdy-list__row--interactive:hover{background:var(--color-surface-2);}
|
|
1094
1094
|
.fdy-list__row--button:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--color-primary);}
|
|
1095
1095
|
.fdy-list__row--interactive{cursor:pointer;}
|
|
1096
|
-
/* Disabled row. The reset above adopts the UA button box, but not its disabled state
|
|
1096
|
+
/* Disabled row. The reset above adopts the UA button box, but not its disabled state, so a row
|
|
1097
1097
|
disabled mid-flight (a redirect in progress, say) kept lighting up and kept a pointer cursor:
|
|
1098
1098
|
a control answering the pointer while refusing input. Same contract as every other disabled
|
|
1099
1099
|
control in the kit (dim + not-allowed), hover withdrawn, both the native and the aria- form. */
|
|
@@ -1106,7 +1106,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1106
1106
|
.fdy-list__aside{flex:none;display:flex;align-items:center;gap:var(--space-2);}
|
|
1107
1107
|
/* Density: rows tighten with the rest of the kit (--space-* mid-range steps under compact). */
|
|
1108
1108
|
|
|
1109
|
-
/* Freeday
|
|
1109
|
+
/* Freeday. Menu (action popup) + Split button. Enhanced by freeday-menu.js. */
|
|
1110
1110
|
.fdy-menu-wrap{position:relative;display:inline-flex;}
|
|
1111
1111
|
.fdy-menu{position:absolute;top:calc(100% + var(--space-1));left:0;z-index:130;min-width:11rem;margin:0;padding:var(--space-1);list-style:none;background:var(--color-surface);color:var(--color-text);border:var(--bw) solid var(--color-border-strong);border-radius:var(--radius-md);box-shadow:var(--shadow-3);}
|
|
1112
1112
|
.fdy-menu[hidden]{display:none;}
|
|
@@ -1114,7 +1114,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1114
1114
|
.fdy-menu__item{display:flex;align-items:center;gap:var(--space-2);width:100%;padding:var(--space-2) var(--space-3);border:0;background:transparent;border-radius:var(--radius-sm);font-family:var(--font-body);font-size:var(--text-sm);color:var(--color-text);text-align:left;cursor:pointer;white-space:nowrap;}
|
|
1115
1115
|
.fdy-menu__item:hover,.fdy-menu__item.is-highlighted{background:var(--color-surface-3);}
|
|
1116
1116
|
/* Focus is not hover. The enhancer moves real DOM focus (freeday-menu.js `.focus()`), so this is
|
|
1117
|
-
the only thing a keyboard user has to go on
|
|
1117
|
+
the only thing a keyboard user has to go on, with the hover fill alone, arrowing through the
|
|
1118
1118
|
menu is invisible. Same ring as .fdy-nav__item, which got this right. Reported twice. */
|
|
1119
1119
|
.fdy-menu__item:focus-visible{outline:none;background:var(--color-surface-3);box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
|
|
1120
1120
|
.fdy-menu__item:disabled{opacity:.5;cursor:not-allowed;background:transparent;}
|
|
@@ -1123,7 +1123,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1123
1123
|
.fdy-menu__sep{height:0;margin:var(--space-1) 0;border:0;border-top:var(--bw) solid var(--color-border-muted);}
|
|
1124
1124
|
@media (prefers-reduced-motion:no-preference){.fdy-menu:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
1125
1125
|
|
|
1126
|
-
/* Split button
|
|
1126
|
+
/* Split button, main action + attached menu toggle (caret) */
|
|
1127
1127
|
.fdy-btn-split{position:relative;display:inline-flex;border-radius:var(--radius-md);transition:transform var(--dur-fast) var(--ease-standard),box-shadow var(--dur-fast) var(--ease-standard);}
|
|
1128
1128
|
/* One control: the two halves must stay coplanar. Neutralize each segment's own
|
|
1129
1129
|
lift + drop-shadow (they would misalign the seam / glow a single half); the
|
|
@@ -1138,7 +1138,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1138
1138
|
.fdy-btn-split__toggle:hover,.fdy-btn-split__toggle:focus-visible{z-index:1;}
|
|
1139
1139
|
.fdy-btn-split__toggle svg{display:block;width:1rem;height:1rem;}
|
|
1140
1140
|
|
|
1141
|
-
/* Freeday
|
|
1141
|
+
/* Freeday. Modal (native <dialog>) */
|
|
1142
1142
|
.fdy-modal{width:min(32rem,calc(100vw - var(--space-8)));max-height:calc(100vh - var(--space-16));padding:0;border:var(--bw) solid var(--color-border);border-radius:var(--radius-xl);background:var(--color-surface);color:var(--color-text);box-shadow:var(--shadow-lift-hover);overflow:hidden;flex-direction:column;opacity:0;transform:translateY(8px) scale(.98);transition:opacity var(--dur-base) var(--ease-spring),transform var(--dur-base) var(--ease-spring),overlay var(--dur-base) allow-discrete,display var(--dur-base) allow-discrete;}
|
|
1143
1143
|
.fdy-modal--sm{width:min(24rem,calc(100vw - var(--space-8)));}
|
|
1144
1144
|
.fdy-modal--md{width:min(32rem,calc(100vw - var(--space-8)));}
|
|
@@ -1161,7 +1161,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1161
1161
|
.fdy-modal[open]::backdrop{opacity:0;}
|
|
1162
1162
|
}
|
|
1163
1163
|
|
|
1164
|
-
/* Freeday
|
|
1164
|
+
/* Freeday. Pagination */
|
|
1165
1165
|
.fdy-pagination__list{display:flex;align-items:center;gap:var(--space-1);list-style:none;margin:0;padding:0;}
|
|
1166
1166
|
.fdy-pagination__link{display:inline-flex;align-items:center;justify-content:center;min-width:2.25rem;height:2.25rem;padding:0 var(--space-2);border-radius:var(--radius-md);font-size:var(--text-sm);font-weight:var(--weight-medium);color:var(--color-text);background:transparent;border:var(--bw) solid transparent;text-decoration:none;cursor:pointer;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
|
|
1167
1167
|
.fdy-pagination__link:hover{background:var(--color-surface-2);border-color:var(--color-border);}
|
|
@@ -1170,7 +1170,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1170
1170
|
.fdy-pagination__link[aria-disabled="true"],.fdy-pagination__link:disabled{opacity:.5;cursor:not-allowed;pointer-events:none;}
|
|
1171
1171
|
.fdy-pagination__ellipsis{display:inline-flex;align-items:center;justify-content:center;min-width:2.25rem;height:2.25rem;color:var(--color-text-subtle);}
|
|
1172
1172
|
|
|
1173
|
-
/* Freeday
|
|
1173
|
+
/* Freeday. Progress */
|
|
1174
1174
|
.fdy-progress{height:.5rem;border-radius:var(--radius-full);background:var(--color-surface-3);overflow:hidden;}
|
|
1175
1175
|
.fdy-progress__bar{height:100%;border-radius:inherit;background:var(--color-primary);transition:width var(--dur-base) var(--ease-standard);}
|
|
1176
1176
|
.fdy-progress--indeterminate{position:relative;}
|
|
@@ -1178,7 +1178,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1178
1178
|
@keyframes fdy-progress-indet{0%{margin-left:-40%;}100%{margin-left:100%;}}
|
|
1179
1179
|
@media (prefers-reduced-motion:reduce){.fdy-progress--indeterminate .fdy-progress__bar{animation:none;width:100%;opacity:.5;}}
|
|
1180
1180
|
|
|
1181
|
-
/* Freeday
|
|
1181
|
+
/* Freeday. Rating (star rating). Interactive = native radio group (arrow keys, form-assoc);
|
|
1182
1182
|
* filled vs empty differ by fill-vs-outline (not colour alone). Enhanced by freeday-rating.js. */
|
|
1183
1183
|
.fdy-rating{display:inline-flex;gap:var(--space-1);}
|
|
1184
1184
|
.fdy-rating__star{display:inline-flex;line-height:0;cursor:pointer;}
|
|
@@ -1191,17 +1191,17 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1191
1191
|
.fdy-rating--disabled .fdy-rating__star{cursor:not-allowed;}
|
|
1192
1192
|
.fdy-rating--disabled .fdy-rating__star svg{opacity:.5;}
|
|
1193
1193
|
/* Invalid: tint the (outline) empty stars danger so a required-but-empty rating reads as an error,
|
|
1194
|
-
paired with a help/error message
|
|
1194
|
+
paired with a help/error message, never colour alone. */
|
|
1195
1195
|
.fdy-rating--error .fdy-rating__star svg{stroke:var(--color-danger);}
|
|
1196
1196
|
.fdy-rating--sm .fdy-rating__star svg{width:1.1rem;height:1.1rem;}
|
|
1197
1197
|
|
|
1198
|
-
/* Freeday
|
|
1198
|
+
/* Freeday. Selection controls */
|
|
1199
1199
|
/* `flex:none` on every box, track and standalone checkbox is load-bearing, not tidiness.
|
|
1200
1200
|
`.fdy-check` is an inline-flex row of [box, label], and a flex item with a WIDTH is still
|
|
1201
|
-
shrinkable
|
|
1201
|
+
shrinkable, so as soon as the label wrapped to a second line the box gave up horizontal space
|
|
1202
1202
|
to it and rendered an 18px-tall rectangle 13px wide. Squashed, off-square, and different from
|
|
1203
1203
|
every sibling whose label happened to be short: a form of five checkboxes showed three sizes.
|
|
1204
|
-
Nothing in the declarations looked wrong, because the width was never overridden
|
|
1204
|
+
Nothing in the declarations looked wrong, because the width was never overridden, it was
|
|
1205
1205
|
negotiated away. */
|
|
1206
1206
|
.fdy-check,.fdy-radio,.fdy-switch{display:inline-flex;align-items:center;gap:var(--space-2);font-size:var(--text-sm);color:var(--color-text);cursor:pointer;}
|
|
1207
1207
|
.fdy-check input,.fdy-radio input{appearance:none;-webkit-appearance:none;flex:none;width:var(--control-box);height:var(--control-box);margin:0;border:1.5px solid var(--color-control-border);background:var(--color-surface);box-shadow:inset 0 1px 2px var(--color-shadow-inset);cursor:pointer;display:grid;place-content:center;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
|
|
@@ -1219,7 +1219,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1219
1219
|
.fdy-switch input:checked{background:var(--color-primary);}
|
|
1220
1220
|
.fdy-switch input:checked::before{transform:translateX(calc(var(--control-switch-w) - var(--control-switch-h)));}
|
|
1221
1221
|
|
|
1222
|
-
/* Standalone checkbox (e.g. table row/select-all)
|
|
1222
|
+
/* Standalone checkbox (e.g. table row/select-all), supports :indeterminate */
|
|
1223
1223
|
.fdy-checkbox{appearance:none;-webkit-appearance:none;flex:none;width:var(--control-box);height:var(--control-box);margin:0;border:1.5px solid var(--color-control-border);border-radius:var(--radius-sm);background:var(--color-surface);box-shadow:inset 0 1px 2px var(--color-shadow-inset);cursor:pointer;display:grid;place-content:center;transition:background var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
|
|
1224
1224
|
.fdy-checkbox::before{content:"";width:.62rem;height:.62rem;transform:scale(0);transition:transform var(--dur-fast) var(--ease-spring);box-shadow:inset 1rem 1rem var(--color-on-primary);clip-path:polygon(14% 44%,0 65%,50% 100%,100% 16%,80% 0,43% 62%);}
|
|
1225
1225
|
.fdy-checkbox:checked,.fdy-checkbox:indeterminate{background:var(--color-primary);border-color:var(--color-primary);}
|
|
@@ -1228,7 +1228,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1228
1228
|
.fdy-checkbox:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
1229
1229
|
.fdy-checkbox:disabled{opacity:.5;cursor:not-allowed;}
|
|
1230
1230
|
|
|
1231
|
-
/* Freeday
|
|
1231
|
+
/* Freeday. Skeleton */
|
|
1232
1232
|
.fdy-skeleton{display:block;border-radius:var(--radius-sm);background:var(--color-surface-3);background-image:linear-gradient(90deg,transparent,color-mix(in srgb,var(--color-surface) 60%,transparent),transparent);background-size:200% 100%;background-repeat:no-repeat;animation:fdy-skeleton 1.4s ease-in-out infinite;}
|
|
1233
1233
|
.fdy-skeleton--text{height:.8rem;margin:.3rem 0;}
|
|
1234
1234
|
.fdy-skeleton--title{height:1.2rem;width:40%;}
|
|
@@ -1241,7 +1241,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1241
1241
|
@keyframes fdy-skeleton{to{background-position:-200% 0;}}
|
|
1242
1242
|
@media (prefers-reduced-motion:reduce){.fdy-skeleton{animation:none;}}
|
|
1243
1243
|
|
|
1244
|
-
/* Freeday
|
|
1244
|
+
/* Freeday. Slider (native <input type="range"> themed via accent-color: keeps native
|
|
1245
1245
|
* keyboard + ARIA, filled track and thumb come free). Optional value output + scale.
|
|
1246
1246
|
* freeday-slider.js just mirrors the value into an <output for="…">. */
|
|
1247
1247
|
.fdy-slider{width:100%;max-width:22rem;height:1.5rem;accent-color:var(--color-primary);cursor:pointer;}
|
|
@@ -1254,14 +1254,14 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1254
1254
|
.fdy-slider__value{font-family:var(--font-mono);font-size:var(--text-sm);font-weight:var(--weight-semibold);color:var(--color-primary-strong);font-variant-numeric:tabular-nums;}
|
|
1255
1255
|
.fdy-slider__scale{display:flex;justify-content:space-between;font-size:var(--text-xs);color:var(--color-text-muted);font-variant-numeric:tabular-nums;}
|
|
1256
1256
|
|
|
1257
|
-
/* Freeday
|
|
1257
|
+
/* Freeday. Spinner */
|
|
1258
1258
|
.fdy-spinner{display:inline-block;width:1.5rem;height:1.5rem;border:2.5px solid var(--color-border-strong);border-top-color:var(--color-primary);border-radius:var(--radius-full);animation:fdy-spin .7s linear infinite;}
|
|
1259
1259
|
.fdy-spinner--sm{width:1rem;height:1rem;border-width:2px;}
|
|
1260
1260
|
.fdy-spinner--lg{width:2.5rem;height:2.5rem;border-width:3px;}
|
|
1261
1261
|
@keyframes fdy-spin{to{transform:rotate(360deg);}}
|
|
1262
1262
|
@media (prefers-reduced-motion:reduce){.fdy-spinner{animation-duration:2s;}}
|
|
1263
1263
|
|
|
1264
|
-
/* Freeday
|
|
1264
|
+
/* Freeday. States (empty / loading / error placeholders) */
|
|
1265
1265
|
.fdy-state{display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--space-2);padding:var(--space-10) var(--space-6);}
|
|
1266
1266
|
.fdy-state__icon{display:inline-flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;color:var(--color-text-subtle);}
|
|
1267
1267
|
.fdy-state__icon svg{width:100%;height:100%;}
|
|
@@ -1270,7 +1270,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1270
1270
|
.fdy-state__actions{margin-top:var(--space-2);display:flex;gap:var(--space-2);flex-wrap:wrap;justify-content:center;}
|
|
1271
1271
|
.fdy-state--danger .fdy-state__icon{color:var(--color-danger-strong);}
|
|
1272
1272
|
|
|
1273
|
-
/* Freeday
|
|
1273
|
+
/* Freeday. Stepper / Wizard (progress header + optional panels).
|
|
1274
1274
|
* Enhanced by freeday-stepper.js for prev/next navigation. */
|
|
1275
1275
|
.fdy-stepper{display:flex;list-style:none;margin:0;padding:0;}
|
|
1276
1276
|
.fdy-step{flex:1;position:relative;display:flex;flex-direction:column;align-items:center;text-align:center;min-width:0;}
|
|
@@ -1280,7 +1280,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1280
1280
|
.fdy-step__btn{position:relative;z-index:1;display:flex;flex-direction:column;align-items:center;gap:var(--space-2);padding:0 var(--space-2);background:none;border:0;font:inherit;color:inherit;cursor:pointer;max-width:100%;}
|
|
1281
1281
|
.fdy-step__btn:disabled{cursor:default;}
|
|
1282
1282
|
/* The marker carries the lift itself, NOT `__btn`. The connector above is position:absolute with
|
|
1283
|
-
z-index:0, and a positioned box paints after in-flow inline content in the same stacking context
|
|
1283
|
+
z-index:0, and a positioned box paints after in-flow inline content in the same stacking context,
|
|
1284
1284
|
so a marker that is merely inline (an indicator-only stepper with no `__btn`) gets painted THROUGH
|
|
1285
1285
|
by the connector line. Lifting the part that must always exist makes that assembly impossible;
|
|
1286
1286
|
`__btn`'s own z-index stays, harmlessly, for the navigable case. */
|
|
@@ -1297,8 +1297,8 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1297
1297
|
.fdy-step-panels{margin-top:var(--space-5);}
|
|
1298
1298
|
.fdy-step-nav{display:flex;justify-content:space-between;gap:var(--space-2);margin-top:var(--space-5);}
|
|
1299
1299
|
|
|
1300
|
-
/* Freeday
|
|
1301
|
-
/* position:relative on every scroller/clipper
|
|
1300
|
+
/* Freeday. Table */
|
|
1301
|
+
/* position:relative on every scroller/clipper, it makes the box the containing block for its own
|
|
1302
1302
|
absolutely positioned content (.fdy-visually-hidden above all), which `overflow` alone does NOT
|
|
1303
1303
|
clip. Without it a hidden label in a wide table scrolls the whole page. See base.css. */
|
|
1304
1304
|
.fdy-table-wrap{position:relative;overflow-x:auto;border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);box-shadow:var(--shadow-1);background:var(--color-surface);}
|
|
@@ -1309,7 +1309,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1309
1309
|
.fdy-table tbody tr:last-child td{border-bottom:0;}
|
|
1310
1310
|
.fdy-table tbody tr{transition:background-color var(--dur-fast) var(--ease-standard);}
|
|
1311
1311
|
.fdy-table tbody tr:hover{background:var(--color-surface-2);}
|
|
1312
|
-
/* Monospace data
|
|
1312
|
+
/* Monospace data, ids, codes, IPs, timestamps. Alignment-neutral so it works in any cell
|
|
1313
1313
|
or inline (`<span class="fdy-mono">`), not just numeric columns. */
|
|
1314
1314
|
.fdy-mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums;}
|
|
1315
1315
|
/* Numeric cells are mono + right-aligned. Kept self-contained (not composed from .fdy-mono)
|
|
@@ -1336,7 +1336,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1336
1336
|
.fdy-table__selcol{width:1px;white-space:nowrap;}
|
|
1337
1337
|
.fdy-table tbody tr[aria-selected="true"],.fdy-table tbody tr[aria-selected="true"]:hover{background:var(--color-primary-soft);}
|
|
1338
1338
|
|
|
1339
|
-
/* Frozen header row and/or first column
|
|
1339
|
+
/* Frozen header row and/or first column, a rate matrix, a timetable, any grid read
|
|
1340
1340
|
against two axes at once.
|
|
1341
1341
|
|
|
1342
1342
|
This modifier exists because `position:sticky` alone is NOT enough. With
|
|
@@ -1350,12 +1350,12 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1350
1350
|
/* Freezing the HEADER ROW is its own modifier because it needs a vertical
|
|
1351
1351
|
scrollport (`.fdy-table-scroll--frozen`). A table that only freezes columns
|
|
1352
1352
|
scrolls with the PAGE, and `top:0` against the page sticks the header under
|
|
1353
|
-
the viewport edge
|
|
1353
|
+
the viewport edge, over the app's own top bar, which is worse than not
|
|
1354
1354
|
freezing it. Ask for the axis you actually have. */
|
|
1355
1355
|
.fdy-table--sticky-head thead th{position:sticky;top:0;z-index:2;border-bottom:var(--bw) solid var(--color-border);}
|
|
1356
1356
|
|
|
1357
1357
|
/* Freezing a COLUMN: put `.fdy-table__freeze` on every cell of it, header
|
|
1358
|
-
included, and give each frozen column its own `--fdy-freeze-left
|
|
1358
|
+
included, and give each frozen column its own `--fdy-freeze-left`. 0 for the
|
|
1359
1359
|
first, the summed widths of the frozen columns left of it after that. One
|
|
1360
1360
|
frozen column needs no variable at all. Mark the last one `--edge` so the
|
|
1361
1361
|
reader can see where the frozen block ends.
|
|
@@ -1364,12 +1364,12 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1364
1364
|
widths, which CSS cannot sum, and hard-coding them here would only work for
|
|
1365
1365
|
tables whose columns are all the same width. */
|
|
1366
1366
|
.fdy-table--sticky .fdy-table__freeze{position:sticky;left:var(--fdy-freeze-left,0);}
|
|
1367
|
-
/* A frozen body cell slides OVER the data, so it must be OPAQUE
|
|
1367
|
+
/* A frozen body cell slides OVER the data, so it must be OPAQUE, otherwise the
|
|
1368
1368
|
cells beneath show through it. A `th` already carries the header tint from the
|
|
1369
1369
|
base rule, and that tint is usually the whole point: it is what marks a column
|
|
1370
1370
|
as identity rather than data. So only a `td` is given a surface here. A table
|
|
1371
|
-
that wants its frozen `th` to read as a body row instead
|
|
1372
|
-
a hovered row should band unbroken across the freeze line
|
|
1371
|
+
that wants its frozen `th` to read as a body row instead, a rate matrix, where
|
|
1372
|
+
a hovered row should band unbroken across the freeze line, says so itself. */
|
|
1373
1373
|
.fdy-table--sticky tbody .fdy-table__freeze{z-index:1;}
|
|
1374
1374
|
.fdy-table--sticky tbody td.fdy-table__freeze{background:var(--color-surface);}
|
|
1375
1375
|
.fdy-table--sticky tbody tr:hover td.fdy-table__freeze{background:var(--color-surface-2);}
|
|
@@ -1392,19 +1392,19 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1392
1392
|
.fdy-table-footer{display:flex;align-items:center;justify-content:space-between;gap:var(--space-3);flex-wrap:wrap;padding:var(--space-3) var(--space-4);border-top:var(--bw) solid var(--color-border);}
|
|
1393
1393
|
.fdy-table-footer__info{font-size:var(--text-sm);color:var(--color-text-muted);}
|
|
1394
1394
|
/* Rows per page. `margin-left:auto` eats the free space, so the size control and the pager travel
|
|
1395
|
-
together on the right while the range stays left
|
|
1395
|
+
together on the right while the range stays left, no wrapper element, which is what keeps the
|
|
1396
1396
|
footer's DOM byte-identical for the tables that don't offer one (#008). The control is the kit's
|
|
1397
1397
|
own `.fdy-combo`, never a native <select>: an OS menu is unthemeable, and on macOS it drops a
|
|
1398
|
-
dark panel into a light page
|
|
1398
|
+
dark panel into a light page, which is the reason FdyCombo exists at all. Sized to its content:
|
|
1399
1399
|
`.fdy-combo` is display:block at max-width 22rem, which in a footer is a bar-wide box for two
|
|
1400
|
-
digits. Its HEIGHT is left alone on purpose
|
|
1400
|
+
digits. Its HEIGHT is left alone on purpose, `--control-h`, the same as every other control on
|
|
1401
1401
|
the page. It briefly carried 2.25rem to match the pager links beside it, which made the one
|
|
1402
1402
|
combobox in a compact app 4px taller than all the others; a pager link is a nav button, and the
|
|
1403
1403
|
thing this must agree with is the form controls. */
|
|
1404
1404
|
.fdy-table-footer__size{display:inline-flex;align-items:center;gap:var(--space-2);margin-left:auto;font-size:var(--text-sm);color:var(--color-text-muted);}
|
|
1405
1405
|
.fdy-table-footer__size .fdy-combo{width:5.5rem;}
|
|
1406
1406
|
|
|
1407
|
-
/* Column filter
|
|
1407
|
+
/* Column filter, funnel button injected (enhancer) or rendered (FdyTable) into a filterable <th> */
|
|
1408
1408
|
.fdy-table__filterwrap{display:inline-flex;align-items:center;vertical-align:middle;}
|
|
1409
1409
|
.fdy-table__filterbtn{appearance:none;display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;margin-left:var(--space-1);padding:0;vertical-align:middle;border:0;border-radius:var(--radius-sm);background:transparent;color:var(--color-text-subtle);cursor:pointer;}
|
|
1410
1410
|
.fdy-table__filterbtn:hover{background:var(--color-surface-3);color:var(--color-text);}
|
|
@@ -1421,7 +1421,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1421
1421
|
.fdy-filter__range .fdy-input{width:6rem;}
|
|
1422
1422
|
.fdy-filter__foot{display:flex;justify-content:space-between;gap:var(--space-2);margin-top:var(--space-3);}
|
|
1423
1423
|
|
|
1424
|
-
/* Bulk-action bar
|
|
1424
|
+
/* Bulk-action bar, appears above the table body once rows are selected */
|
|
1425
1425
|
.fdy-table-bulkbar{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2) var(--space-4);background:var(--color-primary-soft);border-bottom:var(--bw) solid var(--color-border);color:var(--color-primary-strong);font-size:var(--text-sm);}
|
|
1426
1426
|
.fdy-table-bulkbar[hidden]{display:none;}
|
|
1427
1427
|
.fdy-table-bulkbar__count{font-weight:var(--weight-semibold);white-space:nowrap;}
|
|
@@ -1429,14 +1429,14 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1429
1429
|
.fdy-table-bulkbar__actions{display:flex;gap:var(--space-2);}
|
|
1430
1430
|
|
|
1431
1431
|
/* Controls inside a data row. `.fdy-input` and friends are width:100%, which in an auto-layout
|
|
1432
|
-
table means they contribute NO intrinsic width
|
|
1432
|
+
table means they contribute NO intrinsic width, the column shrinks to whatever the header text
|
|
1433
1433
|
needs and the control collapses with it. A NOTE column ends up a box too narrow to read what you
|
|
1434
1434
|
typed into it. The floor is per-control rather than on the column, because the table does not
|
|
1435
1435
|
know which of its columns hold controls. */
|
|
1436
1436
|
.fdy-table td>.fdy-input,.fdy-table td>.fdy-textarea,.fdy-table td>.fdy-combo,.fdy-table td>.fdy-input-group,.fdy-table td>.fdy-datepicker,.fdy-table td>.fdy-timepicker{min-width:7rem;}
|
|
1437
1437
|
|
|
1438
|
-
/* Freeday
|
|
1439
|
-
/* position:relative
|
|
1438
|
+
/* Freeday. Tabs (WAI-ARIA APG) */
|
|
1439
|
+
/* position:relative, containing block for out-of-flow content in the tabs (see base.css). */
|
|
1440
1440
|
.fdy-tabs__list{position:relative;display:flex;gap:var(--space-1);border-bottom:var(--bw) solid var(--color-border);overflow-x:auto;}
|
|
1441
1441
|
.fdy-tabs__tab{appearance:none;border:0;background:transparent;cursor:pointer;white-space:nowrap;font-family:var(--font-body);font-size:var(--text-sm);font-weight:var(--weight-medium);color:var(--color-text-muted);padding:var(--space-3) var(--space-4);border-bottom:2px solid transparent;margin-bottom:-1px;transition:color var(--dur-fast) var(--ease-standard),border-color var(--dur-fast) var(--ease-standard);}
|
|
1442
1442
|
.fdy-tabs__tab:hover{color:var(--color-text);}
|
|
@@ -1444,7 +1444,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1444
1444
|
.fdy-tabs__tab:disabled:hover,.fdy-tabs__tab[aria-disabled="true"]:hover{color:var(--color-text-muted);}
|
|
1445
1445
|
/* `aria-current="page"` is honoured alongside `aria-selected="true"` so the tab look can be used for
|
|
1446
1446
|
ROUTE-driven sub-navigation (/settings/profile · /settings/billing), where the tabs are real links.
|
|
1447
|
-
A link cannot be `aria-selected
|
|
1447
|
+
A link cannot be `aria-selected`, that is invalid ARIA on an anchor; `aria-current` is the only
|
|
1448
1448
|
correct marker, and it is what a router sets. Use `.fdy-tabs` + `freeday-tabs.js` (roving tabindex,
|
|
1449
1449
|
a panel per tab) for in-page tabs; use these classes on plain links for routed ones, and
|
|
1450
1450
|
`.fdy-nav--horizontal` for an application's PRIMARY navigation. */
|
|
@@ -1455,7 +1455,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1455
1455
|
/* Reveal the newly-shown panel on tab switch (fade + rise), reduced-motion gated. */
|
|
1456
1456
|
@media (prefers-reduced-motion:no-preference){.fdy-tabs__panel:not([hidden]){animation:fdy-combo-in var(--dur-base) var(--ease-standard);}}
|
|
1457
1457
|
|
|
1458
|
-
/* Freeday
|
|
1458
|
+
/* Freeday. Timeline (vertical event feed: dot markers on a connector line). Pure CSS. */
|
|
1459
1459
|
.fdy-timeline{list-style:none;margin:0;padding:0;}
|
|
1460
1460
|
.fdy-timeline__item{position:relative;padding:0 0 var(--space-5) var(--space-6);border-left:2px solid var(--color-border);}
|
|
1461
1461
|
.fdy-timeline__item:last-child{border-left-color:transparent;padding-bottom:0;}
|
|
@@ -1468,7 +1468,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1468
1468
|
.fdy-timeline__title{margin:0;font-size:var(--text-sm);font-weight:var(--weight-semibold);color:var(--color-text);}
|
|
1469
1469
|
.fdy-timeline__text{margin:var(--space-1) 0 0;font-size:var(--text-sm);color:var(--color-text-muted);line-height:var(--leading-normal);}
|
|
1470
1470
|
|
|
1471
|
-
/* Freeday
|
|
1471
|
+
/* Freeday. Time picker (input-styled trigger + scrollable time-list popover).
|
|
1472
1472
|
* Built by freeday-timepicker.js from an empty [data-fdy-timepicker] wrapper.
|
|
1473
1473
|
* Trigger visual mirrors the date picker for a consistent control family. */
|
|
1474
1474
|
.fdy-timepicker{position:relative;display:inline-block;width:100%;max-width:11rem;}
|
|
@@ -1492,14 +1492,14 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1492
1492
|
.fdy-timepicker__opt[aria-selected="true"]{background:var(--color-primary-soft);color:var(--color-primary-strong);font-weight:var(--weight-semibold);}
|
|
1493
1493
|
@media (prefers-reduced-motion:no-preference){.fdy-timepicker__panel:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
1494
1494
|
|
|
1495
|
-
/* Freeday
|
|
1495
|
+
/* Freeday. Toast (transient notification) */
|
|
1496
1496
|
/* The region is a POPOVER, not merely a high z-index.
|
|
1497
|
-
`.fdy-modal` is a native <dialog> opened with showModal(), which puts it in the TOP LAYER
|
|
1497
|
+
`.fdy-modal` is a native <dialog> opened with showModal(), which puts it in the TOP LAYER, a
|
|
1498
1498
|
layer above the whole z-index universe, so no number here could ever beat it. A toast raised by
|
|
1499
1499
|
an action taken inside a modal was painted behind that modal's backdrop: dimmed, unreadable, and
|
|
1500
1500
|
most of it under the dialog itself. `freeday-toast.js` calls showPopover() as each toast is
|
|
1501
1501
|
added, which puts the region in the top layer too, and last-in wins there.
|
|
1502
|
-
Everything after `pointer-events` is undoing the UA's [popover] defaults
|
|
1502
|
+
Everything after `pointer-events` is undoing the UA's [popover] defaults, it arrives with
|
|
1503
1503
|
inset:0, auto margins, a border, padding, a Canvas background and overflow:auto, none of which a
|
|
1504
1504
|
toast stack wants. Where showPopover() is unavailable the element is still an ordinary fixed
|
|
1505
1505
|
block at z-index 200, which is exactly what it was before. */
|
|
@@ -1522,14 +1522,14 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1522
1522
|
@media (prefers-reduced-motion:no-preference){.fdy-toast{animation:fdy-toast-in var(--dur-base) var(--ease-spring);}}
|
|
1523
1523
|
@keyframes fdy-toast-in{from{opacity:0;transform:translateY(14px);}to{opacity:1;transform:none;}}
|
|
1524
1524
|
|
|
1525
|
-
/* Freeday
|
|
1525
|
+
/* Freeday. Tooltip (CSS-only, hover/focus) */
|
|
1526
1526
|
.fdy-tooltip-wrap{position:relative;display:inline-flex;}
|
|
1527
1527
|
.fdy-tooltip{position:absolute;bottom:calc(100% + var(--space-2));left:50%;z-index:150;max-width:16rem;padding:var(--space-1) var(--space-2);border-radius:var(--radius-sm);font-size:var(--text-xs);font-weight:var(--weight-medium);line-height:var(--leading-snug);color:var(--color-inverse-text);background:var(--color-inverse-surface);box-shadow:var(--shadow-2);opacity:0;visibility:hidden;pointer-events:none;transform:translateX(-50%) translateY(4px);transition:opacity var(--dur-fast) var(--ease-standard),transform var(--dur-fast) var(--ease-standard);}
|
|
1528
1528
|
.fdy-tooltip::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);border:.3rem solid transparent;border-top-color:var(--color-inverse-surface);}
|
|
1529
1529
|
.fdy-tooltip-wrap:hover .fdy-tooltip,.fdy-tooltip-wrap:focus-within .fdy-tooltip{opacity:1;visibility:visible;transform:translateX(-50%) translateY(0);}
|
|
1530
1530
|
@media (prefers-reduced-motion:reduce){.fdy-tooltip{transition:none;transform:translateX(-50%);}}
|
|
1531
1531
|
|
|
1532
|
-
/* Freeday
|
|
1532
|
+
/* Freeday. Tree view (hierarchy via nested native <details>: keyboard-toggle, zero-JS).
|
|
1533
1533
|
* Branches expand/collapse; leaves are plain rows. Chevron + optional folder/file icons. */
|
|
1534
1534
|
.fdy-tree{list-style:none;margin:0;padding:0;font-size:var(--text-sm);}
|
|
1535
1535
|
.fdy-tree ul{list-style:none;margin:0;padding-left:var(--space-4);}
|
|
@@ -1546,7 +1546,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1546
1546
|
/* Disabled row (branch summary or leaf): dimmed, no hover, not interactive. */
|
|
1547
1547
|
.fdy-tree__leaf[aria-disabled="true"],.fdy-tree__branch[aria-disabled="true"] > summary{opacity:.5;cursor:not-allowed;pointer-events:none;}
|
|
1548
1548
|
|
|
1549
|
-
/* Checkbox variant
|
|
1549
|
+
/* Checkbox variant, a selectable tree (cascading parent/child via freeday-tree.js).
|
|
1550
1550
|
* A native <input class="fdy-checkbox"> sits at the start of every row; a partially
|
|
1551
1551
|
* selected branch shows the native :indeterminate dash. The checkbox stops click
|
|
1552
1552
|
* propagation, so toggling selection never expands/collapses the branch. */
|