@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.bundle.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/* Freeday
|
|
2
|
-
/* Freeday tokens
|
|
1
|
+
/* Freeday, tokens + components in one file. GENERATED by tokens/build.mjs. */
|
|
2
|
+
/* Freeday tokens. GENERATED by tokens/build.mjs. Do not edit by hand. */
|
|
3
3
|
:root {
|
|
4
4
|
--azure-50: #eff4ff;
|
|
5
5
|
--azure-100: #dbe6fe;
|
|
@@ -179,7 +179,7 @@
|
|
|
179
179
|
--focus-ring-width: 2px;
|
|
180
180
|
}
|
|
181
181
|
/* The SYSTEM default stays root-scoped: it is a statement about the document, and dropping :root
|
|
182
|
-
* here would match every element that is not itself [data-theme="light"]
|
|
182
|
+
* here would match every element that is not itself [data-theme="light"], which would re-darken
|
|
183
183
|
* the children of a light panel, since they carry no attribute of their own. */
|
|
184
184
|
@media (prefers-color-scheme: dark) {
|
|
185
185
|
:root:not([data-theme="light"]) {
|
|
@@ -241,7 +241,7 @@
|
|
|
241
241
|
}
|
|
242
242
|
/* The two EXPLICIT opt-ins are deliberately NOT scoped to :root, for the same reason as density
|
|
243
243
|
* below: these are inheriting custom properties, so data-theme on any ancestor re-themes just that
|
|
244
|
-
* subtree
|
|
244
|
+
* subtree, a dark brand panel beside a light form is an ordinary layout, and it should not require
|
|
245
245
|
* re-colouring each component by hand. The root still matches, so data-theme on <html> is unchanged.
|
|
246
246
|
* Both keep the same specificity (0,1,0) as :root and come after it, so they still win there; and
|
|
247
247
|
* --light after --dark means a light island inside a dark region wins in turn. */
|
|
@@ -357,7 +357,7 @@
|
|
|
357
357
|
--chart-tick: var(--slate-400);
|
|
358
358
|
--focus-ring: var(--azure-600);
|
|
359
359
|
}
|
|
360
|
-
/* Density is deliberately NOT scoped to :root. These are custom properties, so they inherit
|
|
360
|
+
/* Density is deliberately NOT scoped to :root. These are custom properties, so they inherit,
|
|
361
361
|
* putting the attribute on any ancestor (a route wrapper, one section) densifies just that
|
|
362
362
|
* subtree, which is how density is actually decided: per screen, not per app. The root still
|
|
363
363
|
* matches, so setting data-density on the html element keeps working exactly as before. */
|
|
@@ -369,7 +369,7 @@
|
|
|
369
369
|
--control-h: 2rem;
|
|
370
370
|
}
|
|
371
371
|
/* ...and the way back out. Inheritance only runs downhill: once the root is compact every subtree is
|
|
372
|
-
* compact, and before this rule existed a data-density="comfortable" wrapper matched nothing
|
|
372
|
+
* compact, and before this rule existed a data-density="comfortable" wrapper matched nothing, so the
|
|
373
373
|
* comment above was true in one direction only. An app that is dense overall and carries one region
|
|
374
374
|
* of shared chrome (a header, a toolbar, a footer shared with a sibling product) had to restate the
|
|
375
375
|
* defaults by hand, which is exactly the local copy that goes stale when the kit retunes them. */
|
|
@@ -381,8 +381,8 @@
|
|
|
381
381
|
--control-h: 2.5rem;
|
|
382
382
|
}
|
|
383
383
|
|
|
384
|
-
/* Freeday components
|
|
385
|
-
/* Freeday base layer
|
|
384
|
+
/* Freeday components. GENERATED by tokens/build.mjs. Do not edit by hand. */
|
|
385
|
+
/* Freeday base layer: reset ringan + default via token */
|
|
386
386
|
*, *::before, *::after { box-sizing: border-box; }
|
|
387
387
|
html { -webkit-text-size-adjust: 100%; }
|
|
388
388
|
body {
|
|
@@ -400,21 +400,21 @@ a { color: var(--color-primary); }
|
|
|
400
400
|
:where(button, input, select, textarea) { font: inherit; }
|
|
401
401
|
/* Hidden from sight, kept for assistive tech. `clip` hides PAINTING, not LAYOUT: the box still
|
|
402
402
|
* has a position, and because it is position:absolute its containing block is the nearest
|
|
403
|
-
* POSITIONED ancestor
|
|
403
|
+
* POSITIONED ancestor, and with none, the initial containing block (the document itself).
|
|
404
404
|
*
|
|
405
405
|
* That matters, because `overflow` only clips a descendant whose containing block is inside the
|
|
406
406
|
* overflow box. So a hidden span in a horizontally scrolling table (the kit's own recommended way
|
|
407
|
-
* to name an icon button) parks at its static position
|
|
407
|
+
* to name an icon button) parks at its static position, possibly thousands of px to the right —
|
|
408
408
|
* and drags the whole DOCUMENT sideways: invisible, and immune to `overflow-x:hidden` on every
|
|
409
409
|
* wrapper. Measured before the fix: 1351px of phantom page scroll from 11 spans.
|
|
410
410
|
*
|
|
411
|
-
* The fix is not here
|
|
411
|
+
* The fix is not here but on the containers: every clipping/scrolling container in the kit
|
|
412
412
|
* that holds consumer markup declares `position:relative` so it becomes the containing block for
|
|
413
413
|
* its own out-of-flow content (see test/css.test.mjs, which will not let a new one skip it).
|
|
414
|
-
* Note the measurement trap: documentElement.scrollWidth sees it, body.scrollWidth does not
|
|
414
|
+
* Note the measurement trap: documentElement.scrollWidth sees it, body.scrollWidth does not,
|
|
415
415
|
* the honest check is `window.scrollTo(9999, 0)` then reading `window.scrollX`. */
|
|
416
416
|
.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; }
|
|
417
|
-
/* Opt-in list reset. base.css is intentionally a *light* reset
|
|
417
|
+
/* Opt-in list reset. base.css is intentionally a *light* reset: it does NOT strip list/paragraph
|
|
418
418
|
* margins (see the kit's own list components, which each reset themselves). If you run a utility
|
|
419
419
|
* framework with its preflight OFF (the recommended pairing), a semantic <ul>/<ol> keeps native
|
|
420
420
|
* bullets + a 40px indent; add this class (or a Freeday list component) to normalise it. */
|
|
@@ -423,10 +423,10 @@ a { color: var(--color-primary); }
|
|
|
423
423
|
*, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
-
/* Freeday
|
|
427
|
-
/* position:relative
|
|
426
|
+
/* Freeday. Accordion (native <details>/<summary>, zero-JS) */
|
|
427
|
+
/* position:relative, containing block for out-of-flow panel content (see base.css). NOT redundant
|
|
428
428
|
with the panel's reveal animation: that animation gives the panel a transform (which happens to
|
|
429
|
-
make it a containing block too), but it lives behind prefers-reduced-motion:no-preference
|
|
429
|
+
make it a containing block too), but it lives behind prefers-reduced-motion:no-preference, so
|
|
430
430
|
without this line the escape bug appears ONLY for readers who asked for reduced motion. */
|
|
431
431
|
.fdy-accordion{position:relative;border:var(--bw) solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;background:var(--color-surface);}
|
|
432
432
|
.fdy-accordion__item + .fdy-accordion__item{border-top:var(--bw) solid var(--color-border);}
|
|
@@ -438,10 +438,10 @@ a { color: var(--color-primary); }
|
|
|
438
438
|
.fdy-accordion__item > summary:focus-visible{outline:none;box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
|
|
439
439
|
.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);}
|
|
440
440
|
/* Content reveal on open (fade + rise). Native <details> can't animate its own height cross-
|
|
441
|
-
* browser without JS, so we animate the revealed panel instead
|
|
441
|
+
* browser without JS, so we animate the revealed panel instead, cheap, safe, reduced-motion gated. */
|
|
442
442
|
@media (prefers-reduced-motion:no-preference){.fdy-accordion__item[open] > .fdy-accordion__panel{animation:fdy-combo-in var(--dur-base) var(--ease-standard);}}
|
|
443
443
|
|
|
444
|
-
/* Freeday
|
|
444
|
+
/* Freeday. Alert (inline) */
|
|
445
445
|
.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);}
|
|
446
446
|
.fdy-alert__icon{flex:none;display:inline-flex;align-items:center;justify-content:center;width:1.25rem;height:1.25rem;margin-top:.05rem;}
|
|
447
447
|
.fdy-alert__icon svg{width:100%;height:100%;}
|
|
@@ -460,17 +460,17 @@ a { color: var(--color-primary); }
|
|
|
460
460
|
.fdy-alert--danger{background:var(--color-danger-soft);border-color:color-mix(in srgb,var(--color-danger) 26%,transparent);}
|
|
461
461
|
.fdy-alert--danger .fdy-alert__title,.fdy-alert--danger .fdy-alert__icon{color:var(--color-danger-strong);}
|
|
462
462
|
|
|
463
|
-
/* Freeday
|
|
463
|
+
/* Freeday. App Shell.
|
|
464
464
|
* Natural document scroll: the shell grows with content and the PAGE scrolls,
|
|
465
465
|
* while the sidebar and topbar are position:sticky so they stay in view. No
|
|
466
466
|
* fixed-height / internal-scroll trickery (that leaves a white gap if the host
|
|
467
467
|
* page can still scroll). Structure: .fdy-app > (.fdy-app__sidebar, .fdy-app__content
|
|
468
468
|
* > (.fdy-app__topbar, .fdy-app__main)). The __content wrapper gives the sticky
|
|
469
|
-
* topbar a tall containing block so it can travel
|
|
469
|
+
* topbar a tall containing block so it can travel, a bare grid cell can't. */
|
|
470
470
|
.fdy-app{display:flex;align-items:stretch;min-height:100vh;background:var(--color-surface-2);}
|
|
471
471
|
.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);}
|
|
472
472
|
.fdy-app__content{flex:1;min-width:0;display:flex;flex-direction:column;}
|
|
473
|
-
/* Brand is a fixed header
|
|
473
|
+
/* Brand is a fixed header, same height as the topbar so they align, and only
|
|
474
474
|
* the nav below it scrolls. Bare text (no children) renders as one bold
|
|
475
475
|
* display line via the rules on .fdy-app__brand itself. For a mark + two-line
|
|
476
476
|
* title/subtitle, add structured children mirroring .fdy-appbar__brand:
|
|
@@ -486,10 +486,10 @@ a { color: var(--color-primary); }
|
|
|
486
486
|
.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;}
|
|
487
487
|
.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;}
|
|
488
488
|
.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);}
|
|
489
|
-
/* Horizontal variant
|
|
489
|
+
/* Horizontal variant, the SAME nav links laid out as a row, for a top-nav application (one that
|
|
490
490
|
* puts its primary navigation in `.fdy-appbar` / `.fdy-app__topbar` and has no sidebar). Deliberately
|
|
491
491
|
* a modifier and not a new block: the item, its states and `aria-current="page"` are unchanged. That
|
|
492
|
-
* is the point
|
|
492
|
+
* is the point, a navigation link is a link marked `aria-current`, never `aria-selected` (invalid
|
|
493
493
|
* ARIA on an anchor) and never a tab role, so borrowing `.fdy-tabs` for routes half-adopts a contract
|
|
494
494
|
* (roving tabindex, a panel per tab) that route links do not honour. position:relative because the
|
|
495
495
|
* row scrolls; see base.css on why a scroller must be a containing block. */
|
|
@@ -501,7 +501,7 @@ a { color: var(--color-primary); }
|
|
|
501
501
|
.fdy-nav__item[aria-current="page"]{background:var(--color-primary-soft);color:var(--color-primary-strong);font-weight:var(--weight-semibold);}
|
|
502
502
|
.fdy-nav__item:focus-visible{outline:none;box-shadow:0 0 0 2px color-mix(in srgb,var(--color-primary) 45%,transparent);}
|
|
503
503
|
/* Sidebar-menu extras: per-node icon (leading), count badge (trailing), and a
|
|
504
|
-
* text label that truncates. Any of these are optional
|
|
504
|
+
* text label that truncates. Any of these are optional, a bare text item works. */
|
|
505
505
|
.fdy-nav__icon{flex:none;display:inline-flex;width:1.15rem;height:1.15rem;}
|
|
506
506
|
.fdy-nav__icon svg{display:block;width:100%;height:100%;}
|
|
507
507
|
.fdy-nav__label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
|
|
@@ -511,7 +511,7 @@ a { color: var(--color-primary); }
|
|
|
511
511
|
.fdy-nav__group{margin:0;}
|
|
512
512
|
.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);}
|
|
513
513
|
/* Prose, and a control name: this is the <summary> of a nav group, at --text-xs uppercase. It is
|
|
514
|
-
the second class found spending the decorative ink on type (after .fdy-eyebrow)
|
|
514
|
+
the second class found spending the decorative ink on type (after .fdy-eyebrow), and it was
|
|
515
515
|
already brightening to --color-text-muted on hover, which is the rule admitting its resting
|
|
516
516
|
state was too faint to read. */
|
|
517
517
|
.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);}
|
|
@@ -520,7 +520,7 @@ a { color: var(--color-primary); }
|
|
|
520
520
|
.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);}
|
|
521
521
|
.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;}
|
|
522
522
|
.fdy-nav__group[open] > .fdy-nav__grouplabel::after{transform:rotate(45deg);}
|
|
523
|
-
/* --flat: calmer, non-collapsible grouped nav
|
|
523
|
+
/* --flat: calmer, non-collapsible grouped nav, hide the caret, drop the
|
|
524
524
|
* inter-group divider and the collapse cursor. Works on native <details> groups
|
|
525
525
|
* (caret just hidden) and on static <div>/<p> groups (never collapsible). */
|
|
526
526
|
.fdy-nav--flat .fdy-nav__grouplabel::after{display:none;}
|
|
@@ -533,7 +533,7 @@ a { color: var(--color-primary); }
|
|
|
533
533
|
.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;}
|
|
534
534
|
.fdy-nav__tree[open] > summary .fdy-nav__caret{transform:rotate(45deg);}
|
|
535
535
|
.fdy-nav__sub{display:flex;flex-direction:column;gap:2px;padding-left:calc(var(--space-3) + 1.15rem);margin-top:2px;}
|
|
536
|
-
/* Collapse toggle (hamburger)
|
|
536
|
+
/* Collapse toggle (hamburger), sits in the topbar; collapses the sidebar. */
|
|
537
537
|
.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);}
|
|
538
538
|
.fdy-app__navtoggle:hover{background:var(--color-surface-2);color:var(--color-text);}
|
|
539
539
|
.fdy-app__navtoggle:focus-visible{outline:none;box-shadow:0 0 0 2px var(--color-primary);}
|
|
@@ -546,7 +546,7 @@ a { color: var(--color-primary); }
|
|
|
546
546
|
.fdy-app__main{flex:1;padding:var(--space-8);}
|
|
547
547
|
.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);}
|
|
548
548
|
.fdy-skip:focus{transform:translateY(0);}
|
|
549
|
-
/* --static: for a bounded/embedded shell (a demo box, not the whole page)
|
|
549
|
+
/* --static: for a bounded/embedded shell (a demo box, not the whole page),
|
|
550
550
|
* drop sticky so nothing escapes the box; sidebar stretches to fill it. */
|
|
551
551
|
.fdy-app--static .fdy-app__sidebar{position:static;height:auto;align-self:stretch;}
|
|
552
552
|
.fdy-app--static .fdy-app__topbar{position:static;}
|
|
@@ -562,7 +562,7 @@ a { color: var(--color-primary); }
|
|
|
562
562
|
}
|
|
563
563
|
@media (prefers-reduced-motion:reduce){.fdy-app__sidebar{transition:none;}}
|
|
564
564
|
|
|
565
|
-
/* Freeday
|
|
565
|
+
/* Freeday. App bar (standalone top bar: brand · actions · spacer).
|
|
566
566
|
* Distinct from .fdy-app__topbar (which is bound to the shell grid). Use anywhere;
|
|
567
567
|
* sticky / elevated / dense / coloured via modifiers. Pure CSS. */
|
|
568
568
|
.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);}
|
|
@@ -575,19 +575,19 @@ a { color: var(--color-primary); }
|
|
|
575
575
|
.fdy-appbar__spacer{flex:1;}
|
|
576
576
|
.fdy-appbar__actions{display:inline-flex;align-items:center;gap:var(--space-1);}
|
|
577
577
|
/* Primary navigation inside the bar: `<nav class="fdy-nav fdy-nav--horizontal">` with
|
|
578
|
-
`.fdy-nav__item` links (see app-shell.css). The bar ships no link class of its own
|
|
578
|
+
`.fdy-nav__item` links (see app-shell.css). The bar ships no link class of its own, a nav link
|
|
579
579
|
is the same component whether the nav is a sidebar column or a top row. */
|
|
580
580
|
/* Controls on a coloured app bar go on-colour (icons stay legible, hover is a light wash) */
|
|
581
581
|
.fdy-appbar--primary .fdy-btn--ghost{background:transparent;border-color:transparent;color:var(--color-on-primary);box-shadow:none;}
|
|
582
582
|
.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;}
|
|
583
583
|
/* Nav links on a coloured bar: full on-colour ink in every state (a dimmed variant would trade away
|
|
584
584
|
the contrast the bar's own token pair guarantees). The current page is carried by the background
|
|
585
|
-
wash plus the semibold weight the base rule already applies
|
|
585
|
+
wash plus the semibold weight the base rule already applies, and by `aria-current` itself. */
|
|
586
586
|
.fdy-appbar--primary .fdy-nav__item{color:var(--color-on-primary);}
|
|
587
587
|
.fdy-appbar--primary .fdy-nav__item:hover{background:color-mix(in srgb,var(--color-on-primary) 16%,transparent);color:var(--color-on-primary);}
|
|
588
588
|
.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);}
|
|
589
589
|
|
|
590
|
-
/* Freeday
|
|
590
|
+
/* Freeday. Autocomplete (text input + filtered suggestion listbox, APG editable combobox).
|
|
591
591
|
* Enhanced by freeday-autocomplete.js. */
|
|
592
592
|
.fdy-autocomplete{position:relative;display:block;max-width:22rem;}
|
|
593
593
|
.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);}
|
|
@@ -600,20 +600,20 @@ a { color: var(--color-primary); }
|
|
|
600
600
|
.fdy-autocomplete__empty[hidden]{display:none;}
|
|
601
601
|
@media (prefers-reduced-motion:no-preference){.fdy-autocomplete__listbox:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
602
602
|
|
|
603
|
-
/* Freeday
|
|
603
|
+
/* Freeday. Avatar */
|
|
604
604
|
.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;}
|
|
605
605
|
.fdy-avatar img{width:100%;height:100%;object-fit:cover;}
|
|
606
606
|
/* --xs exists for an avatar INSIDE a control: `.fdy-btn--sm` is exactly 2rem tall, which is also
|
|
607
607
|
`--sm`'s size, so a small avatar in a small button fills it edge to edge and the ghost border
|
|
608
608
|
crosses the circle. 1.5rem leaves the same ~4px breathing room the kit's controls give their
|
|
609
|
-
icons. Uses `--text-xs` (the smallest type token the kit has
|
|
609
|
+
icons. Uses `--text-xs` (the smallest type token the kit has, there is no `2xs`). */
|
|
610
610
|
.fdy-avatar--xs{width:1.5rem;height:1.5rem;font-size:var(--text-xs);}
|
|
611
611
|
.fdy-avatar--sm{width:2rem;height:2rem;font-size:var(--text-xs);}
|
|
612
612
|
.fdy-avatar--lg{width:3.5rem;height:3.5rem;font-size:var(--text-lg);}
|
|
613
613
|
.fdy-avatar-group{display:inline-flex;}
|
|
614
614
|
.fdy-avatar-group .fdy-avatar{border:2px solid var(--color-surface);margin-left:calc(var(--space-2)*-1);}
|
|
615
615
|
.fdy-avatar-group .fdy-avatar:first-child{margin-left:0;}
|
|
616
|
-
/* Decorative identity tones
|
|
616
|
+
/* Decorative identity tones, a soft tint of a categorical chart colour with a theme-aware,
|
|
617
617
|
* text-leaning foreground so initials keep WCAG AA (verified >=4.5:1 in light & dark by
|
|
618
618
|
* test/contrast.test.mjs). Use to disambiguate same-initial avatars (e.g. many "PT …" names):
|
|
619
619
|
* hash a stable 1..8 index off the FULL name in your app, then apply .fdy-avatar--tone-<n>. */
|
|
@@ -627,7 +627,7 @@ a { color: var(--color-primary); }
|
|
|
627
627
|
.fdy-avatar--tone-7{--_fdy-avatar-tone:var(--tone-7);}
|
|
628
628
|
.fdy-avatar--tone-8{--_fdy-avatar-tone:var(--tone-8);}
|
|
629
629
|
|
|
630
|
-
/* Freeday
|
|
630
|
+
/* Freeday. Badge */
|
|
631
631
|
.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);}
|
|
632
632
|
.fdy-badge--success{color:var(--color-success-strong);background:var(--color-success-soft);border-color:color-mix(in srgb,var(--color-success) 26%,transparent);}
|
|
633
633
|
.fdy-badge--warning{color:var(--color-warning-strong);background:var(--color-warning-soft);border-color:color-mix(in srgb,var(--color-warning) 26%,transparent);}
|
|
@@ -635,12 +635,12 @@ a { color: var(--color-primary); }
|
|
|
635
635
|
.fdy-badge--info{color:var(--color-info-strong);background:var(--color-info-soft);border-color:color-mix(in srgb,var(--color-info) 26%,transparent);}
|
|
636
636
|
.fdy-badge--outline{background:transparent;border-color:var(--color-border-strong);color:var(--color-text-muted);box-shadow:none;}
|
|
637
637
|
|
|
638
|
-
/* Categorical tone badges
|
|
638
|
+
/* Categorical tone badges, a status vocabulary larger than the semantic palette, from the
|
|
639
639
|
* general --tone-1..8 tokens. Same validated AA formula as .fdy-avatar--tone-* and
|
|
640
640
|
* .fdy-chip--tone-* (text >=4.5:1 both themes, gated by test/contrast.test.mjs).
|
|
641
641
|
*
|
|
642
642
|
* Semantics come first: a state that IS good, bad or waiting takes --success / --danger /
|
|
643
|
-
* --warning. These are for the rest
|
|
643
|
+
* --warning. These are for the rest, the states a workflow distinguishes but a palette of five
|
|
644
644
|
* cannot. A back-office document list carrying Draft, Submitted, Approved, Completed, Settled,
|
|
645
645
|
* Closed, Transferred, InDeclaration, Open and Rejected in ONE column collapsed to three colours,
|
|
646
646
|
* which is the report this shipped for. */
|
|
@@ -654,7 +654,7 @@ a { color: var(--color-primary); }
|
|
|
654
654
|
.fdy-badge--tone-7{--_fdy-badge-tone:var(--tone-7);}
|
|
655
655
|
.fdy-badge--tone-8{--_fdy-badge-tone:var(--tone-8);}
|
|
656
656
|
|
|
657
|
-
/* Overlay badge
|
|
657
|
+
/* Overlay badge, anchors a small count/dot/icon badge to a corner of any element
|
|
658
658
|
* (icon, button, text). Wrap the content in .fdy-badge-wrap; add .fdy-badge-ov as a
|
|
659
659
|
* sibling. Default is a solid neutral pill; colour + dot + position via modifiers. */
|
|
660
660
|
.fdy-badge-wrap{position:relative;display:inline-flex;vertical-align:middle;}
|
|
@@ -664,7 +664,7 @@ a { color: var(--color-primary); }
|
|
|
664
664
|
/* Solid colours safe for count/text (on-* tokens, AA in light & dark) */
|
|
665
665
|
.fdy-badge-ov--primary{background:var(--color-primary);color:var(--color-on-primary);}
|
|
666
666
|
.fdy-badge-ov--danger{background:var(--color-danger-btn);color:var(--color-on-danger);}
|
|
667
|
-
/* Dot-oriented accents (for dots
|
|
667
|
+
/* Dot-oriented accents (for dots, plain text on these is not guaranteed AA) */
|
|
668
668
|
.fdy-badge-ov--accent{background:var(--color-accent);}
|
|
669
669
|
.fdy-badge-ov--info{background:var(--color-info);}
|
|
670
670
|
.fdy-badge-ov--success{background:var(--color-success);}
|
|
@@ -674,7 +674,7 @@ a { color: var(--color-primary); }
|
|
|
674
674
|
.fdy-badge-ov--bottom-right{top:auto;bottom:0;transform:translate(50%,50%);}
|
|
675
675
|
.fdy-badge-ov--bottom-left{top:auto;bottom:0;right:auto;left:0;transform:translate(-50%,50%);}
|
|
676
676
|
|
|
677
|
-
/* Freeday
|
|
677
|
+
/* Freeday. Breadcrumb */
|
|
678
678
|
.fdy-breadcrumb{font-size:var(--text-sm);}
|
|
679
679
|
.fdy-breadcrumb__list{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);list-style:none;margin:0;padding:0;}
|
|
680
680
|
.fdy-breadcrumb__item{display:inline-flex;align-items:center;gap:var(--space-2);color:var(--color-text-muted);}
|
|
@@ -684,23 +684,23 @@ a { color: var(--color-primary); }
|
|
|
684
684
|
.fdy-breadcrumb__item[aria-current="page"]{color:var(--color-text);font-weight:var(--weight-medium);}
|
|
685
685
|
.fdy-breadcrumb__sep{color:var(--color-text-subtle);user-select:none;}
|
|
686
686
|
|
|
687
|
-
/* Freeday
|
|
687
|
+
/* Freeday. Responsive display utilities (pairs with freeday-breakpoint.js).
|
|
688
688
|
* Breakpoint scale (min-width, px): sm 600 · md 960 · lg 1280 · xl 1920.
|
|
689
|
-
* NOTE: media-query breakpoints must be literal
|
|
689
|
+
* NOTE: media-query breakpoints must be literal. CSS can't read custom properties
|
|
690
690
|
* inside @media, so these px are structural, not stray magic numbers. */
|
|
691
691
|
.fdy-hidden{display:none !important;}
|
|
692
692
|
|
|
693
|
-
/* hide-below-X
|
|
693
|
+
/* hide-below-X, hidden when the viewport is narrower than X (content shows at X and up) */
|
|
694
694
|
@media (max-width:599.98px){.fdy-hide-below-sm{display:none !important;}}
|
|
695
695
|
@media (max-width:959.98px){.fdy-hide-below-md{display:none !important;}}
|
|
696
696
|
@media (max-width:1279.98px){.fdy-hide-below-lg{display:none !important;}}
|
|
697
697
|
|
|
698
|
-
/* hide-above-X
|
|
698
|
+
/* hide-above-X, hidden when the viewport is X and wider (content shows below X) */
|
|
699
699
|
@media (min-width:600px){.fdy-hide-above-sm{display:none !important;}}
|
|
700
700
|
@media (min-width:960px){.fdy-hide-above-md{display:none !important;}}
|
|
701
701
|
@media (min-width:1280px){.fdy-hide-above-lg{display:none !important;}}
|
|
702
702
|
|
|
703
|
-
/* Freeday
|
|
703
|
+
/* Freeday. Button */
|
|
704
704
|
.fdy-btn{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);
|
|
705
705
|
height:var(--control-h);padding:0 var(--space-4);
|
|
706
706
|
font-family:var(--font-body);font-size:var(--text-sm);font-weight:var(--weight-semibold);
|
|
@@ -713,7 +713,7 @@ a { color: var(--color-primary); }
|
|
|
713
713
|
.fdy-btn:active{transform:translateY(1px);box-shadow:0 1px 4px -1px color-mix(in srgb,var(--color-primary) 46%,transparent);}
|
|
714
714
|
.fdy-btn:disabled{opacity:.5;cursor:not-allowed;transform:none;box-shadow:none;}
|
|
715
715
|
/* Pressed / toggle. `aria-pressed` is already the right attribute for a toggle button, so this adds
|
|
716
|
-
no class and no new markup contract
|
|
716
|
+
no class and no new markup contract, and it is what makes .fdy-btn-group a complete segmented
|
|
717
717
|
control: the group joins the borders, this says which segment you are on. Each fill variant
|
|
718
718
|
defines its OWN pressed look below; a single shared rule cannot work, because a gradient is a
|
|
719
719
|
background-IMAGE and would blank the ghost variant's background-color. Solid: invert the gradient
|
|
@@ -725,12 +725,12 @@ a { color: var(--color-primary); }
|
|
|
725
725
|
.fdy-btn--ghost:hover{transform:none;background:var(--color-primary-soft);border-color:var(--color-primary-border);box-shadow:var(--shadow-1);}
|
|
726
726
|
.fdy-btn--ghost:active{transform:translateY(1px);}
|
|
727
727
|
|
|
728
|
-
/* --danger used to be the SOLID treatment and nothing else, so `fdy-btn--ghost fdy-btn--danger
|
|
729
|
-
which every reader parses as "quiet destructive"
|
|
728
|
+
/* --danger used to be the SOLID treatment and nothing else, so `fdy-btn--ghost fdy-btn--danger`,
|
|
729
|
+
which every reader parses as "quiet destructive", rendered a second SOLID button beside Save.
|
|
730
730
|
That is a hierarchy defect, not a cosmetic one: the base class is already the one primary per
|
|
731
731
|
screen. `.fdy-menu__item--danger` has always modelled the quiet form (red text, not a red block);
|
|
732
732
|
the pairing rules below bring it to the button. They are more specific than this one and come
|
|
733
|
-
after it, so this rule needs no :not() guard of its own
|
|
733
|
+
after it, so this rule needs no :not() guard of its own, one mechanism, not two. */
|
|
734
734
|
.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);}
|
|
735
735
|
.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);}
|
|
736
736
|
.fdy-btn--danger:active{transform:translateY(1px);}
|
|
@@ -744,21 +744,21 @@ a { color: var(--color-primary); }
|
|
|
744
744
|
.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);}
|
|
745
745
|
.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);}
|
|
746
746
|
|
|
747
|
-
/* Icon inside a button
|
|
747
|
+
/* Icon inside a button, scales with the button's font size (works for text+icon & icon-only) */
|
|
748
748
|
.fdy-btn__icon{display:inline-flex;flex:none;}
|
|
749
749
|
.fdy-btn__icon svg{display:block;width:1.15em;height:1.15em;}
|
|
750
750
|
|
|
751
|
-
/* Text button
|
|
751
|
+
/* Text button, lowest emphasis (no fill/border), colored label, subtle hover tint */
|
|
752
752
|
.fdy-btn--text{background:none;color:var(--color-primary-strong);border-color:transparent;box-shadow:none;}
|
|
753
753
|
.fdy-btn--text:hover{transform:none;background:var(--color-primary-soft);box-shadow:none;}
|
|
754
754
|
.fdy-btn--text:active{transform:translateY(1px);box-shadow:none;}
|
|
755
|
-
/* Ghost/text pressed: the kit's selected-surface pair (soft fill + strong ink)
|
|
755
|
+
/* Ghost/text pressed: the kit's selected-surface pair (soft fill + strong ink), the same pairing
|
|
756
756
|
.fdy-nav__item uses for the current page, and one the AA contrast gate already covers. This is
|
|
757
757
|
the realistic segmented control: a row of --ghost buttons with exactly one aria-pressed. */
|
|
758
758
|
.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;}
|
|
759
759
|
.fdy-btn--ghost[aria-pressed="true"]:hover,.fdy-btn--text[aria-pressed="true"]:hover{background:var(--color-primary-soft);transform:none;box-shadow:none;}
|
|
760
760
|
|
|
761
|
-
/* Stretched hit area (#007)
|
|
761
|
+
/* Stretched hit area (#007), spread this button's click target over its nearest positioned
|
|
762
762
|
* ancestor, so "one card, one primary action, one escape hatch" needs no click handler on the
|
|
763
763
|
* <article>: the real control stays a real <button>/<a> and keeps its keyboard semantics.
|
|
764
764
|
* `.fdy-card` is already position:relative, so it is the anchor.
|
|
@@ -767,19 +767,19 @@ a { color: var(--color-primary); }
|
|
|
767
767
|
* a transformed element becomes the containing block for its own absolutely-positioned descendants,
|
|
768
768
|
* so `inset:0` re-anchors from the card to the button's own box the moment the button is hovered or
|
|
769
769
|
* pressed. Measured: mousedown lands on the button, the overlay collapses under the pointer, mouseup
|
|
770
|
-
* lands on some other element, and the browser fires `click` on their common ancestor
|
|
770
|
+
* lands on some other element, and the browser fires `click` on their common ancestor, the button
|
|
771
771
|
* never gets one. It demos fine on hover for --text/--ghost (their hover is already transform:none)
|
|
772
772
|
* and only a real press reveals it; the default and --danger fills break one step earlier, on hover.
|
|
773
773
|
* Press feedback moves to the card, which is the object actually being pressed (see card.css). */
|
|
774
774
|
.fdy-btn--stretch::after{content:'';position:absolute;inset:0;}
|
|
775
775
|
.fdy-btn--stretch:hover,.fdy-btn--stretch:active{transform:none;}
|
|
776
776
|
|
|
777
|
-
/* Icon-only
|
|
777
|
+
/* Icon-only, square; combine with --ghost/--text/--sm/--lg. Requires aria-label. */
|
|
778
778
|
.fdy-btn--icon{width:var(--control-h);padding:0;}
|
|
779
779
|
.fdy-btn--icon.fdy-btn--sm{width:calc(var(--control-h) - var(--space-2));}
|
|
780
780
|
.fdy-btn--icon.fdy-btn--lg{width:calc(var(--control-h) + var(--space-2));}
|
|
781
781
|
|
|
782
|
-
/* Button group
|
|
782
|
+
/* Button group, segmented, joined borders, rounded only at the ends */
|
|
783
783
|
.fdy-btn-group{display:inline-flex;}
|
|
784
784
|
.fdy-btn-group .fdy-btn{border-radius:0;}
|
|
785
785
|
.fdy-btn-group .fdy-btn:not(:first-child){margin-left:calc(var(--bw) * -1);}
|
|
@@ -787,7 +787,7 @@ a { color: var(--color-primary); }
|
|
|
787
787
|
.fdy-btn-group .fdy-btn:first-child{border-top-left-radius:var(--radius-md);border-bottom-left-radius:var(--radius-md);}
|
|
788
788
|
.fdy-btn-group .fdy-btn:last-child{border-top-right-radius:var(--radius-md);border-bottom-right-radius:var(--radius-md);}
|
|
789
789
|
|
|
790
|
-
/* FAB
|
|
790
|
+
/* FAB, circular elevated action button (icon), optional extended pill with label */
|
|
791
791
|
.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);}
|
|
792
792
|
.fdy-fab:hover{transform:translateY(-1px);box-shadow:var(--shadow-4);}
|
|
793
793
|
.fdy-fab:active{transform:translateY(1px);}
|
|
@@ -800,35 +800,35 @@ a { color: var(--color-primary); }
|
|
|
800
800
|
.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);}
|
|
801
801
|
.fdy-fab--accent{background:linear-gradient(180deg,var(--color-accent),color-mix(in srgb,#000 10%,var(--color-accent)));color:var(--color-on-accent);}
|
|
802
802
|
|
|
803
|
-
/* Freeday
|
|
804
|
-
/* position:relative
|
|
803
|
+
/* Freeday. Card */
|
|
804
|
+
/* position:relative, containing block for out-of-flow card content (see base.css); it also gives
|
|
805
805
|
consumers the anchor a corner ribbon/badge needs. */
|
|
806
806
|
.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);}
|
|
807
807
|
.fdy-card--elevated{box-shadow:var(--shadow-lift-hover);}
|
|
808
808
|
.fdy-card--interactive{cursor:pointer;}
|
|
809
809
|
.fdy-card--interactive:hover{box-shadow:var(--shadow-lift-hover);transform:translateY(-3px);}
|
|
810
|
-
/* Pressing a stretched target (see .fdy-btn--stretch) is pressing the CARD
|
|
810
|
+
/* Pressing a stretched target (see .fdy-btn--stretch) is pressing the CARD, the button's own
|
|
811
811
|
nudge had to go, and the object under the pointer is the card. Ordered before the reduced-motion
|
|
812
812
|
block and the disabled rules so both still win at equal specificity. */
|
|
813
813
|
.fdy-card--interactive:has(.fdy-btn--stretch:active){transform:translateY(-1px);}
|
|
814
814
|
/* The global reduced-motion reset kills the ANIMATION; drop the -3px shift too so the card
|
|
815
815
|
* doesn't jump. The shadow still communicates the affordance. */
|
|
816
816
|
@media (prefers-reduced-motion:reduce){.fdy-card--interactive:hover,.fdy-card--interactive:has(.fdy-btn--stretch:active){transform:none;}}
|
|
817
|
-
/* When the whole card IS the control
|
|
818
|
-
* announced
|
|
817
|
+
/* When the whole card IS the control, a real <button> so the row is keyboard-reachable and
|
|
818
|
+
* announced, reset the UA button box. Note: it does NOT reset background/border, because
|
|
819
819
|
* .fdy-card already sets its own surface + border (resetting them here would strip the card). */
|
|
820
820
|
.fdy-card--button{display:block;width:100%;text-align:inherit;color:inherit;appearance:none;-webkit-appearance:none;}
|
|
821
|
-
/* Disabled card-as-control
|
|
821
|
+
/* Disabled card-as-control, same contract as .fdy-list__row--button: dim + not-allowed, and the
|
|
822
822
|
--interactive lift is withdrawn so a refusing control stops answering the pointer. */
|
|
823
823
|
.fdy-card--button:disabled,.fdy-card--button[aria-disabled="true"],.fdy-card--interactive[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
|
|
824
824
|
.fdy-card--interactive:disabled:hover,.fdy-card--interactive[aria-disabled="true"]:hover{box-shadow:var(--shadow-lift);transform:none;}
|
|
825
|
-
/* Escape hatches stay clickable above a stretched target
|
|
825
|
+
/* Escape hatches stay clickable above a stretched target, automatic, not a documented convention,
|
|
826
826
|
because forgetting it fails SILENTLY: the secondary control looks and hovers exactly as before and
|
|
827
827
|
simply never receives the click, which is the very symptom the pattern exists to cure. Matched by
|
|
828
828
|
shape (anything focusable) so a control the kit does not ship yet is covered too. */
|
|
829
829
|
: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;}
|
|
830
830
|
/* The position is a DEFAULT, not an override: z-index needs a positioned box (or a flex/grid item),
|
|
831
|
-
but an app that deliberately pins a control inside the card
|
|
831
|
+
but an app that deliberately pins a control inside the card, a corner dismiss, a favourite star,
|
|
832
832
|
must keep its own `position:absolute`. Wrapped in :where() so this whole selector weighs nothing
|
|
833
833
|
and ANY app rule wins it; measured, because at normal specificity the kit silently dragged an
|
|
834
834
|
absolutely positioned dismiss button back into the flow. Do not merge these two rules. */
|
|
@@ -838,9 +838,9 @@ a { color: var(--color-primary); }
|
|
|
838
838
|
.fdy-card__desc{font-size:var(--text-sm);color:var(--color-text-muted);margin:0;line-height:var(--leading-normal);}
|
|
839
839
|
.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;}
|
|
840
840
|
|
|
841
|
-
/* Freeday
|
|
841
|
+
/* Freeday. Carousel (scroll-snap track + arrows + dots). Enhanced by freeday-carousel.js. */
|
|
842
842
|
.fdy-carousel{position:relative;}
|
|
843
|
-
/* position:relative
|
|
843
|
+
/* position:relative means the arrows are positioned against .fdy-carousel (the parent), so this only
|
|
844
844
|
catches out-of-flow content inside the SLIDES, which would otherwise escape the viewport
|
|
845
845
|
entirely and scroll the page by one slide-offset per slide (see base.css). */
|
|
846
846
|
.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;}
|
|
@@ -859,10 +859,10 @@ a { color: var(--color-primary); }
|
|
|
859
859
|
.fdy-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 40%,transparent);}
|
|
860
860
|
@media (prefers-reduced-motion:reduce){.fdy-carousel__viewport{scroll-behavior:auto;}}
|
|
861
861
|
|
|
862
|
-
/* Freeday
|
|
862
|
+
/* Freeday. Cascade select (hierarchical drill-down picker).
|
|
863
863
|
* Built by freeday-cascade.js from a [data-fdy-cascade] wrapper whose hidden
|
|
864
864
|
* nested <ul> is the data model: a <li> with a child <ul> is a branch, one
|
|
865
|
-
* without is a selectable leaf. The popover shows one level at a time
|
|
865
|
+
* without is a selectable leaf. The popover shows one level at a time, branches
|
|
866
866
|
* drill in, a back control ascends, a leaf selects (value = full path).
|
|
867
867
|
* Trigger visual matches the combo / date / time control family. */
|
|
868
868
|
.fdy-cascade{position:relative;display:inline-block;width:100%;max-width:22rem;}
|
|
@@ -899,16 +899,16 @@ a { color: var(--color-primary); }
|
|
|
899
899
|
.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);}
|
|
900
900
|
@media (prefers-reduced-motion:no-preference){.fdy-cascade__panel:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
901
901
|
|
|
902
|
-
/* Freeday
|
|
902
|
+
/* Freeday. Choose-from-list dialog (searchable master-data picker).
|
|
903
903
|
* Built on .fdy-modal: header + search + scrollable results table + footer.
|
|
904
904
|
* Backs a choose-from-list field (readonly input + .fdy-input-group__btn magnifier). */
|
|
905
905
|
.fdy-modal--cfl{width:min(46rem,calc(100vw - var(--space-8)));}
|
|
906
906
|
.fdy-modal--cfl .fdy-modal__body{padding:0;display:flex;flex-direction:column;min-height:0;overflow:hidden;}
|
|
907
907
|
|
|
908
|
-
/* Search bar
|
|
908
|
+
/* Search bar, single leading-icon field; Enter/typing filters the results. */
|
|
909
909
|
.fdy-cfl__search{flex:none;padding:var(--space-4) var(--space-5);border-bottom:var(--bw) solid var(--color-border-muted);}
|
|
910
910
|
|
|
911
|
-
/* Results
|
|
911
|
+
/* Results, dense rows, sticky header, its own scroll region. */
|
|
912
912
|
.fdy-cfl__results{overflow:auto;max-height:min(24rem,50vh);}
|
|
913
913
|
.fdy-cfl__results .fdy-table th{position:sticky;top:0;z-index:1;}
|
|
914
914
|
.fdy-cfl__row{cursor:pointer;}
|
|
@@ -923,12 +923,12 @@ a { color: var(--color-primary); }
|
|
|
923
923
|
.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;}
|
|
924
924
|
.fdy-cfl__empty[hidden]{display:none;}
|
|
925
925
|
|
|
926
|
-
/* Footer
|
|
926
|
+
/* Footer, selection count (multi) sits left, actions right. */
|
|
927
927
|
.fdy-modal--cfl .fdy-modal__footer{justify-content:space-between;}
|
|
928
928
|
.fdy-cfl__count{font-size:var(--text-sm);color:var(--color-text-muted);}
|
|
929
929
|
.fdy-cfl__actions{display:flex;gap:var(--space-2);}
|
|
930
930
|
|
|
931
|
-
/* Freeday
|
|
931
|
+
/* Freeday. Charts (zero-dependency CSS/SVG, rendered by freeday-chart.js).
|
|
932
932
|
* Colour comes from --fdy-chart (a token, set inline by the renderer from data-fdy-color). */
|
|
933
933
|
|
|
934
934
|
/* Sparkline / trend line (SVG) */
|
|
@@ -948,7 +948,7 @@ a { color: var(--color-primary); }
|
|
|
948
948
|
/* Hover emphasis: dim all bars while the chart is hovered, keep the hovered one lit. */
|
|
949
949
|
@media (hover:hover){.fdy-bars:hover .fdy-bars__bar{opacity:.4;}.fdy-bars__col:hover .fdy-bars__bar{opacity:1;}}
|
|
950
950
|
|
|
951
|
-
/* Interactive tooltip (shared by bar + donut)
|
|
951
|
+
/* Interactive tooltip (shared by bar + donut). Chart.js-style hover readout. */
|
|
952
952
|
.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);}
|
|
953
953
|
.fdy-chart__tip.is-visible{opacity:1;}
|
|
954
954
|
|
|
@@ -969,7 +969,7 @@ a { color: var(--color-primary); }
|
|
|
969
969
|
/* Horizontal legend for cartesian charts (legend sits above the plot). */
|
|
970
970
|
.fdy-chart__legend--row{flex-direction:row;flex-wrap:wrap;gap:var(--space-2) var(--space-4);}
|
|
971
971
|
|
|
972
|
-
/* Cartesian charts (line / area / multi-series & stacked bar)
|
|
972
|
+
/* Cartesian charts (line / area / multi-series & stacked bar). SVG with themed axes.
|
|
973
973
|
* Axis colour comes from --chart-grid / --chart-tick (both theme-aware); series colours are
|
|
974
974
|
* set inline by the renderer from --chart-1..8, so the whole chart recolours on data-theme. */
|
|
975
975
|
.fdy-chart-xy{position:relative;display:flex;flex-direction:column;gap:var(--space-3);width:100%;--fdy-chart-tick-size:var(--text-xs);}
|
|
@@ -984,10 +984,10 @@ a { color: var(--color-primary); }
|
|
|
984
984
|
.fdy-chart-xy__bar{transition:opacity var(--dur-fast) var(--ease-standard);}
|
|
985
985
|
.fdy-chart-xy__band{fill:transparent;cursor:pointer;}
|
|
986
986
|
|
|
987
|
-
/* Freeday
|
|
987
|
+
/* Freeday. Chip (tag / removable filter) */
|
|
988
988
|
.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);}
|
|
989
989
|
.fdy-chip--primary{color:var(--color-primary-strong);background:var(--color-primary-soft);border-color:color-mix(in srgb,var(--color-primary) 22%,transparent);}
|
|
990
|
-
/* Categorical tone chips
|
|
990
|
+
/* Categorical tone chips, non-semantic category tags / legend swatches, from the general
|
|
991
991
|
* --tone-1..8 palette. Same validated AA formula as .fdy-avatar--tone-* (text >=4.5:1 both themes,
|
|
992
992
|
* gated by test/contrast.test.mjs). */
|
|
993
993
|
.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));}
|
|
@@ -1015,14 +1015,14 @@ a { color: var(--color-primary); }
|
|
|
1015
1015
|
.fdy-chip__check svg{display:block;width:100%;height:100%;}
|
|
1016
1016
|
.fdy-chip--filter[aria-pressed="true"] .fdy-chip__check{display:inline-flex;}
|
|
1017
1017
|
|
|
1018
|
-
/* Freeday
|
|
1018
|
+
/* Freeday. Combobox (custom styled select, WAI-ARIA APG select-only) */
|
|
1019
1019
|
.fdy-combo{position:relative;display:block;max-width:22rem;}
|
|
1020
1020
|
.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);}
|
|
1021
1021
|
.fdy-combo__button:hover{border-color:var(--color-text-muted);}
|
|
1022
1022
|
.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);}
|
|
1023
1023
|
.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);}
|
|
1024
1024
|
.fdy-combo__button:disabled,.fdy-combo__button[aria-disabled="true"]{opacity:.5;cursor:not-allowed;}
|
|
1025
|
-
/* Read-only: stays focusable & shows its value at full contrast but can't open
|
|
1025
|
+
/* Read-only: stays focusable & shows its value at full contrast but can't open, muted surface,
|
|
1026
1026
|
softened border, default cursor, no focus-ring escalation (matches input[readonly], #39). */
|
|
1027
1027
|
.fdy-combo__button[aria-readonly="true"]{background:var(--color-surface-2);border-color:var(--color-border);cursor:default;}
|
|
1028
1028
|
.fdy-combo__button[aria-readonly="true"]:hover{border-color:var(--color-border);}
|
|
@@ -1046,7 +1046,7 @@ a { color: var(--color-primary); }
|
|
|
1046
1046
|
.fdy-combo__option.is-highlighted[aria-selected="true"]{box-shadow:inset 0 0 0 2px color-mix(in srgb,var(--color-primary) 55%,transparent);}
|
|
1047
1047
|
/* The tick is CSS, not content (#004 §2). Inside role="option" it joined the option's ACCESSIBLE
|
|
1048
1048
|
NAME, so the selected option announced "✓August" while every other announced "August": the state
|
|
1049
|
-
was read twice
|
|
1049
|
+
was read twice, once as aria-selected, once as decoration, and a test asking for an option by
|
|
1050
1050
|
its own label stopped matching the one that was selected. The `/ ""` is alt text: the glyph is
|
|
1051
1051
|
painted, the accessibility tree gets nothing. Same reasoning as .fdy-label--required (1.29.0).
|
|
1052
1052
|
The empty box still reserves its width, so labels stay aligned whether selected or not. */
|
|
@@ -1055,11 +1055,11 @@ a { color: var(--color-primary); }
|
|
|
1055
1055
|
@media (prefers-reduced-motion:no-preference){.fdy-combo__listbox:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
1056
1056
|
@keyframes fdy-combo-in{from{opacity:0;transform:translateY(-4px);}to{opacity:1;transform:none;}}
|
|
1057
1057
|
|
|
1058
|
-
/* Freeday
|
|
1058
|
+
/* Freeday. Page composition & type roles.
|
|
1059
1059
|
*
|
|
1060
1060
|
* The kit stops at the component; these primitives encode *how a page is assembled* so screens
|
|
1061
1061
|
* built by different people cohere. They carry the usage doctrine (see USAGE.md) in markup: a
|
|
1062
|
-
* named type role gets used, a doc gets skimmed. Everything here is token-driven layout
|
|
1062
|
+
* named type role gets used, a doc gets skimmed. Everything here is token-driven layout, no new
|
|
1063
1063
|
* colours, no new shadows. Pair with .fdy-app (the shell) and the components inside.
|
|
1064
1064
|
*/
|
|
1065
1065
|
|
|
@@ -1068,7 +1068,7 @@ a { color: var(--color-primary); }
|
|
|
1068
1068
|
* page title (the mistake that collapses three levels into one grey mush). */
|
|
1069
1069
|
/* --color-text-muted, not -subtle: an eyebrow is a small label naming the thing below it, i.e.
|
|
1070
1070
|
prose. -subtle is gated at 3.0 on purpose (placeholders, separators, decorative glyphs) and
|
|
1071
|
-
measures 4.41 on the page ground
|
|
1071
|
+
measures 4.41 on the page ground, axe reports it as a serious color-contrast failure. Same
|
|
1072
1072
|
argument, same ink, same 4.41 as .fdy-stat__label in 1.30.0. */
|
|
1073
1073
|
.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;}
|
|
1074
1074
|
.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;}
|
|
@@ -1102,19 +1102,19 @@ a { color: var(--color-primary); }
|
|
|
1102
1102
|
.fdy-stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:var(--space-5);}
|
|
1103
1103
|
.fdy-stat{display:flex;flex-direction:column;gap:var(--space-1);min-width:0;}
|
|
1104
1104
|
/* --color-text-muted, not -subtle: this is a LABEL, i.e. text, and -subtle is deliberately gated at
|
|
1105
|
-
3.0 (placeholders, dividers, decorative glyphs). On surface-2 -subtle measures 4.41
|
|
1105
|
+
3.0 (placeholders, dividers, decorative glyphs). On surface-2 -subtle measures 4.41, under AA
|
|
1106
1106
|
for text, and a stat label is often the only thing naming the number above it. */
|
|
1107
1107
|
.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);}
|
|
1108
1108
|
.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;}
|
|
1109
1109
|
.fdy-stat__value small{font-size:var(--text-lg);font-weight:var(--weight-semibold);color:var(--color-text-muted);}
|
|
1110
1110
|
.fdy-stat__meta{font-size:var(--text-sm);color:var(--color-text-muted);display:flex;align-items:center;gap:var(--space-2);}
|
|
1111
|
-
/* A stat value is display type sized for a COUNT
|
|
1111
|
+
/* A stat value is display type sized for a COUNT, "1,284", and a back office puts money in it.
|
|
1112
1112
|
"IDR 300,000.00" needs 224px at --text-3xl and the grid's own track is 11rem, so it wrapped
|
|
1113
1113
|
between the currency and the number: two lines of 31px where the design says one. Measured, not
|
|
1114
1114
|
guessed (#020).
|
|
1115
1115
|
The value shrinks only when its own column is too narrow to hold it, so a wide dashboard keeps
|
|
1116
1116
|
exactly the type it has today. Scoped to a stat INSIDE .fdy-stats, where the width comes from the
|
|
1117
|
-
grid track
|
|
1117
|
+
grid track, `container-type:inline-size` on a standalone .fdy-stat would stop it sizing to its
|
|
1118
1118
|
own content. The unconditional rule above stays as the fallback wherever @container is missing. */
|
|
1119
1119
|
@supports (container-type:inline-size){
|
|
1120
1120
|
.fdy-stats>.fdy-stat{container-type:inline-size;}
|
|
@@ -1127,7 +1127,7 @@ a { color: var(--color-primary); }
|
|
|
1127
1127
|
/* Icon box (#001 §4). The kit ships no icons on purpose, but every icon slot it DOES ship
|
|
1128
1128
|
(.fdy-btn__icon, .fdy-nav__icon, .fdy-state__icon) assumes something sensible inside, so a
|
|
1129
1129
|
standalone icon had no contract and every app invented one. 1em keeps the glyph matched to the
|
|
1130
|
-
text beside it at every scale
|
|
1130
|
+
text beside it at every scale, bring your own paths, use our box. Works on the <svg> itself or
|
|
1131
1131
|
on a wrapper around it. */
|
|
1132
1132
|
.fdy-icon{display:inline-block;width:1em;height:1em;flex:none;vertical-align:-0.125em;}
|
|
1133
1133
|
.fdy-icon>svg{display:block;width:100%;height:100%;}
|
|
@@ -1140,13 +1140,13 @@ a { color: var(--color-primary); }
|
|
|
1140
1140
|
.fdy-text-warning{color:var(--color-warning-strong);}
|
|
1141
1141
|
.fdy-text-danger{color:var(--color-danger-strong);}
|
|
1142
1142
|
|
|
1143
|
-
/* Freeday
|
|
1143
|
+
/* Freeday. Date picker (input-styled trigger + calendar popover).
|
|
1144
1144
|
* Built by freeday-datepicker.js from an empty [data-fdy-datepicker] wrapper. */
|
|
1145
1145
|
.fdy-datepicker{position:relative;display:inline-block;width:100%;max-width:14rem;}
|
|
1146
1146
|
/* Inside a labelled field, a picker fills the field like every other control.
|
|
1147
1147
|
The 14rem/11rem caps above suit a picker standing on its own; in a form grid
|
|
1148
|
-
they leave the date box visibly short of the combo beside it
|
|
1149
|
-
319px in a two-column layout
|
|
1148
|
+
they leave the date box visibly short of the combo beside it. 224px against
|
|
1149
|
+
319px in a two-column layout, and the row reads as ragged. The field's own
|
|
1150
1150
|
max-width is what should govern there. */
|
|
1151
1151
|
.fdy-field>.fdy-datepicker,.fdy-field>.fdy-timepicker,.fdy-field>.fdy-daterange{max-width:none;}
|
|
1152
1152
|
.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);}
|
|
@@ -1192,12 +1192,12 @@ a { color: var(--color-primary); }
|
|
|
1192
1192
|
.fdy-cal__nav:hover{background:var(--color-surface-2);color:var(--color-text);}
|
|
1193
1193
|
.fdy-cal__nav:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
1194
1194
|
.fdy-cal__grid{display:grid;grid-template-columns:repeat(7,2rem);gap:2px;}
|
|
1195
|
-
/* Month grid
|
|
1195
|
+
/* Month grid. 3×4 over the same track width as the 7-column day grid, so the panel does not resize
|
|
1196
1196
|
when you drill and the pointer does not have to travel.
|
|
1197
1197
|
|
|
1198
1198
|
AFTER `.fdy-cal__grid`, and that is the whole rule: the element carries both classes, they weigh
|
|
1199
1199
|
the same, so source order decides. Declared earlier, the modifier lost to the base it modifies and
|
|
1200
|
-
twelve months rendered seven across
|
|
1200
|
+
twelve months rendered seven across, the layout the comment above described, never once applied. */
|
|
1201
1201
|
.fdy-cal__grid--months,.fdy-cal__grid--years{grid-template-columns:repeat(3,1fr);gap:var(--space-1);}
|
|
1202
1202
|
.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;}
|
|
1203
1203
|
.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;}
|
|
@@ -1215,14 +1215,14 @@ a { color: var(--color-primary); }
|
|
|
1215
1215
|
/* Responsive: each picker may shrink (min-width:0) & wrap so the range never overflows a
|
|
1216
1216
|
narrow row/phone; inline-flex still hugs to ~23rem at wider widths (look unchanged). */
|
|
1217
1217
|
.fdy-daterange{display:inline-flex;flex-wrap:wrap;align-items:center;gap:var(--space-2);}
|
|
1218
|
-
/* Each picker shrinks from an 11rem basis but not below ~7rem
|
|
1218
|
+
/* Each picker shrinks from an 11rem basis but not below ~7rem, enough for "dd/mm/yyyy" + the icon,
|
|
1219
1219
|
so a squeezed range compresses gracefully (and wraps) instead of clipping the date. */
|
|
1220
1220
|
.fdy-daterange .fdy-datepicker{flex:0 1 11rem;min-width:7rem;}
|
|
1221
1221
|
.fdy-daterange__sep{flex:none;color:var(--color-text-subtle);}
|
|
1222
1222
|
|
|
1223
1223
|
@media (prefers-reduced-motion:no-preference){.fdy-datepicker__panel:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
1224
1224
|
|
|
1225
|
-
/* Freeday
|
|
1225
|
+
/* Freeday. Datetime picker (composed: date picker + time picker → one value).
|
|
1226
1226
|
* A thin composer (freeday-datetime.js) links a child [data-fdy-datepicker] and
|
|
1227
1227
|
* [data-fdy-timepicker] and emits a single combined event. Icon variants come from
|
|
1228
1228
|
* the child pickers (data-fdy-no-icon / <template data-fdy-icon>). */
|
|
@@ -1231,18 +1231,18 @@ a { color: var(--color-primary); }
|
|
|
1231
1231
|
.fdy-datetimepicker .fdy-datepicker{width:12rem;max-width:none;}
|
|
1232
1232
|
.fdy-datetimepicker .fdy-timepicker{width:8.5rem;max-width:none;}
|
|
1233
1233
|
|
|
1234
|
-
/* Freeday
|
|
1234
|
+
/* Freeday. Description list (key-value detail view) */
|
|
1235
1235
|
.fdy-dl{display:grid;grid-template-columns:auto 1fr;gap:var(--space-2) var(--space-5);margin:0;}
|
|
1236
1236
|
.fdy-dl dt{color:var(--color-text-muted);font-size:var(--text-sm);}
|
|
1237
1237
|
.fdy-dl dd{margin:0;color:var(--color-text);font-size:var(--text-sm);font-weight:var(--weight-medium);}
|
|
1238
1238
|
.fdy-dl--rows{grid-template-columns:1fr;gap:var(--space-1);}
|
|
1239
1239
|
.fdy-dl--rows dt{margin-top:var(--space-2);}
|
|
1240
1240
|
|
|
1241
|
-
/* Freeday
|
|
1241
|
+
/* Freeday. Divider */
|
|
1242
1242
|
.fdy-divider{border:0;border-top:var(--bw) solid var(--color-border);margin:var(--space-4) 0;}
|
|
1243
1243
|
.fdy-divider--vertical{border-top:0;border-left:var(--bw) solid var(--color-border);margin:0 var(--space-4);align-self:stretch;}
|
|
1244
1244
|
|
|
1245
|
-
/* Freeday
|
|
1245
|
+
/* Freeday. Drawer (temporary side panel on native <dialog>: focus-trap / Esc /
|
|
1246
1246
|
* backdrop / return-focus are free). Anchors left (default) or right.
|
|
1247
1247
|
* Enhanced by freeday-drawer.js (open trigger + backdrop-click close). */
|
|
1248
1248
|
/* display is gated behind [open] so the native <dialog> stays hidden until opened.
|
|
@@ -1269,7 +1269,7 @@ a { color: var(--color-primary); }
|
|
|
1269
1269
|
.fdy-drawer[open]::backdrop{opacity:0;}
|
|
1270
1270
|
}
|
|
1271
1271
|
|
|
1272
|
-
/* Freeday
|
|
1272
|
+
/* Freeday. File upload (drop target + explicit per-file states).
|
|
1273
1273
|
* Enhanced by freeday-upload.js; states: idle · drag-over · uploading · done · error. */
|
|
1274
1274
|
.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);}
|
|
1275
1275
|
.fdy-dropzone:hover{border-color:var(--color-text-muted);}
|
|
@@ -1281,17 +1281,17 @@ a { color: var(--color-primary); }
|
|
|
1281
1281
|
.fdy-dropzone__icon svg{display:block;width:2rem;height:2rem;}
|
|
1282
1282
|
.fdy-dropzone__title{font-size:inherit;font-weight:var(--weight-medium);color:var(--color-text);margin:0;}
|
|
1283
1283
|
.fdy-dropzone__hint{font-size:var(--text-xs);}
|
|
1284
|
-
/* Wide (horizontal) layout
|
|
1284
|
+
/* Wide (horizontal) layout, icon beside the copy instead of stacked, for full-width
|
|
1285
1285
|
* sections. Wrap the title + hint in .fdy-dropzone__text. */
|
|
1286
1286
|
.fdy-dropzone--row{flex-direction:row;align-items:center;justify-content:center;text-align:left;gap:var(--space-4);}
|
|
1287
1287
|
.fdy-dropzone__text{display:flex;flex-direction:column;gap:var(--space-1);}
|
|
1288
1288
|
|
|
1289
1289
|
/* File rows (list of added/uploaded/rejected files) */
|
|
1290
|
-
/* Symmetric on purpose. The list carried margin-top and nothing below, so whatever follows it
|
|
1291
|
-
an "Add files" button, in every upload UI there is
|
|
1290
|
+
/* Symmetric on purpose. The list carried margin-top and nothing below, so whatever follows it,
|
|
1291
|
+
an "Add files" button, in every upload UI there is, sat flush against the last row: measured
|
|
1292
1292
|
0px. A block that claims space on one side only is not spacing, it is a lean. */
|
|
1293
1293
|
.fdy-filelist{display:flex;flex-direction:column;gap:var(--space-2);margin-top:var(--space-3);margin-bottom:var(--space-3);}
|
|
1294
|
-
/* Nothing after it, nothing to separate from
|
|
1294
|
+
/* Nothing after it, nothing to separate from, so the room is not spent. */
|
|
1295
1295
|
.fdy-filelist:last-child{margin-bottom:0;}
|
|
1296
1296
|
/* Side-by-side rows on wide screens (reflow to one column when narrow). */
|
|
1297
1297
|
.fdy-filelist--grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(16rem,1fr));}
|
|
@@ -1310,7 +1310,7 @@ a { color: var(--color-primary); }
|
|
|
1310
1310
|
.fdy-file--error .fdy-file__icon{color:var(--color-danger);}
|
|
1311
1311
|
.fdy-file--error .fdy-file__sub{color:var(--color-danger-strong);}
|
|
1312
1312
|
|
|
1313
|
-
/* Freeday
|
|
1313
|
+
/* Freeday. Filter bar (a consistent row of filters: search + selects + actions).
|
|
1314
1314
|
* Solves the "ragged filter row": apps improvised flex rows with ad-hoc
|
|
1315
1315
|
* flex/min-width, so grow fields ballooned while selects stayed tiny and no two
|
|
1316
1316
|
* screens matched. Here every field snaps to a shared width rhythm; one field may
|
|
@@ -1319,7 +1319,7 @@ a { color: var(--color-primary); }
|
|
|
1319
1319
|
.fdy-filterbar{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--space-3) var(--space-4);}
|
|
1320
1320
|
/* Composed with a table toolbar (one element, both classes): the fields decide the alignment.
|
|
1321
1321
|
* `.fdy-table-toolbar` sets align-items:center (right for bare buttons) at equal specificity, and
|
|
1322
|
-
* being later in the bundle it would otherwise win
|
|
1322
|
+
* being later in the bundle it would otherwise win, floating labelled fields' neighbours out of line.
|
|
1323
1323
|
* This 2-class selector (0,2,0) settles it toward the filterbar's flex-end regardless of source order. */
|
|
1324
1324
|
.fdy-table-toolbar.fdy-filterbar{align-items:flex-end;}
|
|
1325
1325
|
/* Fields are direct children. Drop the standalone field's 22rem cap; default to the
|
|
@@ -1327,7 +1327,7 @@ a { color: var(--color-primary); }
|
|
|
1327
1327
|
.fdy-filterbar>.fdy-field{max-width:none;flex:0 0 auto;width:12rem;}
|
|
1328
1328
|
/* Controls fill their field's column (drop the standalone input-group/combo caps). */
|
|
1329
1329
|
.fdy-filterbar>.fdy-field .fdy-input-group,.fdy-filterbar>.fdy-field .fdy-combo{max-width:none;width:100%;}
|
|
1330
|
-
/* Column-width rhythm
|
|
1330
|
+
/* Column-width rhythm, pick one per field so bars across the app line up. */
|
|
1331
1331
|
.fdy-filterbar>.fdy-field--w-sm{width:9rem;}
|
|
1332
1332
|
.fdy-filterbar>.fdy-field--w-lg{width:15rem;}
|
|
1333
1333
|
.fdy-filterbar>.fdy-field--w-xl{width:18rem;}
|
|
@@ -1337,11 +1337,11 @@ a { color: var(--color-primary); }
|
|
|
1337
1337
|
.fdy-filterbar>.fdy-field--w-grow{flex:1 1 16rem;width:auto;min-width:12rem;}
|
|
1338
1338
|
/* Trailing actions (buttons) pushed to the end, aligned with the controls' baseline.
|
|
1339
1339
|
* Wrap note: a wrapped bar breaks flex lines on each item's *flex-basis* (the grow field's
|
|
1340
|
-
* `flex:1 1 16rem` reserves 16rem for line-breaking), NOT its rendered width
|
|
1340
|
+
* `flex:1 1 16rem` reserves 16rem for line-breaking), NOT its rendered width, so the grow basis,
|
|
1341
1341
|
* not the visible width, decides the wrap point. Margins can't keep the actions off a lonely wrapped
|
|
1342
1342
|
* row; to avoid stranded actions, tune the --w-grow basis or drop a field so the bar fits one row. */
|
|
1343
|
-
/* A control that carries NO label of its own
|
|
1344
|
-
button
|
|
1343
|
+
/* A control that carries NO label of its own, a checkbox, a switch, a lone
|
|
1344
|
+
button, in a bar whose `align-items:flex-end` exists to line up LABELLED
|
|
1345
1345
|
fields. Flex-end puts such a control's bottom on the inputs' bottom, so a
|
|
1346
1346
|
20px checkbox sits well below the 32px input's centre line and reads as
|
|
1347
1347
|
dropped. Giving it the height of a control lands its box exactly where an
|
|
@@ -1349,7 +1349,7 @@ a { color: var(--color-primary); }
|
|
|
1349
1349
|
line. Raised from IDU_EMATE_APPL_WEB (#014). */
|
|
1350
1350
|
.fdy-filterbar>.fdy-check,.fdy-filterbar>.fdy-radio,.fdy-filterbar>.fdy-switch,.fdy-filterbar>.fdy-btn{min-height:var(--control-h);}
|
|
1351
1351
|
.fdy-filterbar__actions{display:flex;align-items:center;gap:var(--space-2);margin-left:auto;}
|
|
1352
|
-
/* Opt-in: sit the actions inline right after the last field instead of at the far edge
|
|
1352
|
+
/* Opt-in: sit the actions inline right after the last field instead of at the far edge, so when the
|
|
1353
1353
|
* bar wraps they read as attached to the filters rather than shoved to the right of a lonely row. */
|
|
1354
1354
|
.fdy-filterbar--actions-inline .fdy-filterbar__actions{margin-left:0;}
|
|
1355
1355
|
/* Stack on narrow screens so nothing overflows the row. */
|
|
@@ -1358,16 +1358,16 @@ a { color: var(--color-primary); }
|
|
|
1358
1358
|
.fdy-filterbar__actions{width:100%;margin-left:0;}
|
|
1359
1359
|
}
|
|
1360
1360
|
|
|
1361
|
-
/* Freeday
|
|
1361
|
+
/* Freeday. Form grid (document/header fields laid out in aligned columns).
|
|
1362
1362
|
* The two-dimensional counterpart to the single-column .fdy-field stack and the
|
|
1363
1363
|
* one-row .fdy-filterbar: related fields sit in columns that reflow to one on narrow
|
|
1364
1364
|
* screens. Fields are the standard .fdy-field; a field with --full spans the whole row
|
|
1365
|
-
* (e.g. an address or note). Zero JS
|
|
1365
|
+
* (e.g. an address or note). Zero JS, pure layout over .fdy-field. */
|
|
1366
1366
|
.fdy-form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(14rem,1fr));gap:var(--space-4) var(--space-5);}
|
|
1367
1367
|
.fdy-form-grid>.fdy-field{max-width:none;}
|
|
1368
1368
|
.fdy-form-grid>.fdy-field--full{grid-column:1/-1;}
|
|
1369
1369
|
|
|
1370
|
-
/* Freeday
|
|
1370
|
+
/* Freeday. Input group (prefix/suffix addons, e.g. Rp / % / icon) */
|
|
1371
1371
|
.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);}
|
|
1372
1372
|
.fdy-input-group:hover{border-color:var(--color-text-muted);}
|
|
1373
1373
|
.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);}
|
|
@@ -1380,34 +1380,34 @@ a { color: var(--color-primary); }
|
|
|
1380
1380
|
.fdy-input-group .fdy-input:focus{box-shadow:none;}
|
|
1381
1381
|
.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);}
|
|
1382
1382
|
.fdy-input-group__addon:last-child{border-right:0;border-left:var(--bw) solid var(--color-border);}
|
|
1383
|
-
/* Borderless leading icon (e.g. search magnifier)
|
|
1383
|
+
/* Borderless leading icon (e.g. search magnifier), self-contained like __btn so it
|
|
1384
1384
|
centers standalone (no base .fdy-input-group__addon required). */
|
|
1385
1385
|
.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);}
|
|
1386
1386
|
.fdy-input-group__addon--icon svg{display:block;width:1rem;height:1rem;}
|
|
1387
|
-
/* The leading icon already provides the left inset
|
|
1387
|
+
/* The leading icon already provides the left inset, don't let the input add its full left padding on top. */
|
|
1388
1388
|
.fdy-input-group__addon--icon + .fdy-input{padding-left:var(--space-2);}
|
|
1389
|
-
/* Trailing action button (e.g. choose-from-list magnifier)
|
|
1389
|
+
/* Trailing action button (e.g. choose-from-list magnifier), a real, focusable control. */
|
|
1390
1390
|
.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);}
|
|
1391
1391
|
.fdy-input-group__btn:hover{background:var(--color-surface-3);color:var(--color-text);}
|
|
1392
1392
|
.fdy-input-group__btn:focus-visible{outline:none;color:var(--color-text);box-shadow:inset 0 0 0 2px var(--color-primary);}
|
|
1393
1393
|
.fdy-input-group__btn svg{display:block;width:1.1rem;height:1.1rem;}
|
|
1394
1394
|
/* Leading action button (the "−" of a number stepper): the divider belongs on the other side. */
|
|
1395
1395
|
.fdy-input-group__btn:first-child{border-left:0;border-right:var(--bw) solid var(--color-border);}
|
|
1396
|
-
/* At a bound, or on a disabled/read-only field, the button must look as inert as it acts
|
|
1396
|
+
/* At a bound, or on a disabled/read-only field, the button must look as inert as it acts, same dim
|
|
1397
1397
|
+ not-allowed as .fdy-input:disabled. :hover still matches a disabled button, so it is withdrawn. */
|
|
1398
1398
|
.fdy-input-group__btn:disabled{opacity:.5;cursor:not-allowed;}
|
|
1399
1399
|
.fdy-input-group__btn:disabled:hover{background:var(--color-surface-2);color:var(--color-text-muted);}
|
|
1400
|
-
/* Error state must win over the neutralizing rules above
|
|
1400
|
+
/* Error state must win over the neutralizing rules above, promote to the wrapper. */
|
|
1401
1401
|
.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);}
|
|
1402
1402
|
.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);}
|
|
1403
1403
|
|
|
1404
|
-
/* Freeday
|
|
1404
|
+
/* Freeday. Field & Input */
|
|
1405
1405
|
.fdy-field{display:flex;flex-direction:column;gap:var(--space-2);max-width:22rem;}
|
|
1406
|
-
/* A grouped field is legitimately a <fieldset> (it needs a <legend>)
|
|
1406
|
+
/* A grouped field is legitimately a <fieldset> (it needs a <legend>), e.g. a radio/checkbox group.
|
|
1407
1407
|
Neutralise the UA groove border / padding / min-inline-size so it lays out identically to the
|
|
1408
1408
|
<div> form (min-inline-size:min-content would otherwise block the filterbar column widths). */
|
|
1409
1409
|
fieldset.fdy-field{border:0;margin:0;padding:0;min-inline-size:0;}
|
|
1410
|
-
/* The rendered legend is laid out outside the fieldset's flex flow, so `gap` never reaches it
|
|
1410
|
+
/* The rendered legend is laid out outside the fieldset's flex flow, so `gap` never reaches it,
|
|
1411
1411
|
measured 0px between legend and first control. This is the spacing contract a grouped field
|
|
1412
1412
|
needs; it matches .fdy-field's own gap so the group reads as one rhythm. */
|
|
1413
1413
|
fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
@@ -1425,40 +1425,40 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1425
1425
|
.fdy-input[readonly]:hover,.fdy-textarea[readonly]:hover{border-color:var(--color-border);}
|
|
1426
1426
|
.fdy-input[readonly]:focus-visible,.fdy-textarea[readonly]:focus-visible{border-color:var(--color-border-strong);box-shadow:none;}
|
|
1427
1427
|
/* Numbers (#44): the user agent draws its own spin buttons inside the field, in OS widget colours no
|
|
1428
|
-
theme reaches
|
|
1428
|
+
theme reaches, on a dark surface they read as a light-grey artefact glued to a themed field, and
|
|
1429
1429
|
it is the only unthemed control on the page. Removed so .fdy-input looks the same whatever its
|
|
1430
1430
|
type. `appearance:textfield` is the Firefox half, the pseudo-element the Blink/WebKit half; both
|
|
1431
|
-
are needed. This trades away the mouse-only increment
|
|
1431
|
+
are needed. This trades away the mouse-only increment, arrow keys on a focused number input still
|
|
1432
1432
|
work, and [data-fdy-number] gives the affordance back on the kit's terms. */
|
|
1433
1433
|
.fdy-input[type="number"]{appearance:textfield;-webkit-appearance:textfield;}
|
|
1434
1434
|
.fdy-input[type="number"]::-webkit-outer-spin-button,.fdy-input[type="number"]::-webkit-inner-spin-button{appearance:none;-webkit-appearance:none;margin:0;}
|
|
1435
1435
|
.fdy-input--error,.fdy-input[aria-invalid="true"],.fdy-textarea[aria-invalid="true"]{border-color:var(--color-danger);}
|
|
1436
1436
|
.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);}
|
|
1437
1437
|
/* Required marker. The control already carries `required`, so the asterisk is decoration: the
|
|
1438
|
-
`/ ""` is CSS alt text
|
|
1438
|
+
`/ ""` is CSS alt text, the glyph is painted but exposes NOTHING to the accessibility tree, so a
|
|
1439
1439
|
screen reader does not read "star" after every label. That is stricter than a <span> an app has to
|
|
1440
1440
|
remember to mark aria-hidden, and it cannot be forgotten. */
|
|
1441
1441
|
.fdy-label--required::after{content:"*" / "";margin-left:var(--space-1);color:var(--color-danger);}
|
|
1442
1442
|
.fdy-help{font-size:var(--text-xs);color:var(--color-text-muted);}
|
|
1443
1443
|
.fdy-help--error{color:var(--color-danger);}
|
|
1444
1444
|
|
|
1445
|
-
/* Freeday
|
|
1445
|
+
/* Freeday. Keyboard key */
|
|
1446
1446
|
.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);}
|
|
1447
1447
|
|
|
1448
|
-
/* Freeday
|
|
1448
|
+
/* Freeday. List: the FLAT row container.
|
|
1449
1449
|
*
|
|
1450
1450
|
* USAGE.md §3 puts list rows among the surfaces that should be flat, but until now the only
|
|
1451
|
-
* container the kit shipped was .fdy-card
|
|
1451
|
+
* container the kit shipped was .fdy-card, which carries --shadow-lift (a real 34px lift). A
|
|
1452
1452
|
* responsive table that becomes a list below `md` therefore had to choose between a stack of
|
|
1453
1453
|
* shadowed cards (against the doctrine) or a hand-built box, which needs a colour and so escapes
|
|
1454
1454
|
* the token system. This is that missing container: one flat surface, hairline dividers, no shadow.
|
|
1455
1455
|
*
|
|
1456
1456
|
* Not to be confused with .fdy-list-reset (base.css), which only strips UA bullets/indent.
|
|
1457
1457
|
* Works on <ul>/<ol> (list-style is reset here) or on plain <div>s. */
|
|
1458
|
-
/* position:relative
|
|
1458
|
+
/* position:relative, containing block for out-of-flow row content (see base.css). */
|
|
1459
1459
|
.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;}
|
|
1460
1460
|
/* A row that hosts a stretched target has to be the anchor itself (#008 §1). Without this the
|
|
1461
|
-
overlay resolves against .fdy-list
|
|
1461
|
+
overlay resolves against .fdy-list, which IS positioned, so EVERY row's target covers the whole
|
|
1462
1462
|
list and the last one in the DOM wins: measured, a click on row one opens row two, under row one's
|
|
1463
1463
|
name, which looks exactly like working software. Opt-in via :has(), so a row that doesn't use the
|
|
1464
1464
|
pattern is untouched and nothing an app absolutely positions inside a plain row starts resolving
|
|
@@ -1467,7 +1467,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1467
1467
|
.fdy-list__row{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-4) var(--space-5);min-width:0;}
|
|
1468
1468
|
/* Divider between rows. Two shapes are supported and both are load-bearing: rows as direct children
|
|
1469
1469
|
* of the list, and rows wrapped in <li> (the semantic shape, where the ADJACENT siblings are the
|
|
1470
|
-
* <li>s
|
|
1470
|
+
* <li>s, a bare `.fdy-list__row + .fdy-list__row` silently matches nothing there). */
|
|
1471
1471
|
.fdy-list__row + .fdy-list__row,
|
|
1472
1472
|
.fdy-list > * + * > .fdy-list__row{border-top:var(--bw) solid var(--color-border-muted);}
|
|
1473
1473
|
/* A row that is itself the control: render it as <button>/<a> and the UA box is reset without
|
|
@@ -1476,7 +1476,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1476
1476
|
.fdy-list__row--button:hover,.fdy-list__row--interactive:hover{background:var(--color-surface-2);}
|
|
1477
1477
|
.fdy-list__row--button:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--color-primary);}
|
|
1478
1478
|
.fdy-list__row--interactive{cursor:pointer;}
|
|
1479
|
-
/* Disabled row. The reset above adopts the UA button box, but not its disabled state
|
|
1479
|
+
/* Disabled row. The reset above adopts the UA button box, but not its disabled state, so a row
|
|
1480
1480
|
disabled mid-flight (a redirect in progress, say) kept lighting up and kept a pointer cursor:
|
|
1481
1481
|
a control answering the pointer while refusing input. Same contract as every other disabled
|
|
1482
1482
|
control in the kit (dim + not-allowed), hover withdrawn, both the native and the aria- form. */
|
|
@@ -1489,7 +1489,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1489
1489
|
.fdy-list__aside{flex:none;display:flex;align-items:center;gap:var(--space-2);}
|
|
1490
1490
|
/* Density: rows tighten with the rest of the kit (--space-* mid-range steps under compact). */
|
|
1491
1491
|
|
|
1492
|
-
/* Freeday
|
|
1492
|
+
/* Freeday. Menu (action popup) + Split button. Enhanced by freeday-menu.js. */
|
|
1493
1493
|
.fdy-menu-wrap{position:relative;display:inline-flex;}
|
|
1494
1494
|
.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);}
|
|
1495
1495
|
.fdy-menu[hidden]{display:none;}
|
|
@@ -1497,7 +1497,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1497
1497
|
.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;}
|
|
1498
1498
|
.fdy-menu__item:hover,.fdy-menu__item.is-highlighted{background:var(--color-surface-3);}
|
|
1499
1499
|
/* Focus is not hover. The enhancer moves real DOM focus (freeday-menu.js `.focus()`), so this is
|
|
1500
|
-
the only thing a keyboard user has to go on
|
|
1500
|
+
the only thing a keyboard user has to go on, with the hover fill alone, arrowing through the
|
|
1501
1501
|
menu is invisible. Same ring as .fdy-nav__item, which got this right. Reported twice. */
|
|
1502
1502
|
.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);}
|
|
1503
1503
|
.fdy-menu__item:disabled{opacity:.5;cursor:not-allowed;background:transparent;}
|
|
@@ -1506,7 +1506,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1506
1506
|
.fdy-menu__sep{height:0;margin:var(--space-1) 0;border:0;border-top:var(--bw) solid var(--color-border-muted);}
|
|
1507
1507
|
@media (prefers-reduced-motion:no-preference){.fdy-menu:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
1508
1508
|
|
|
1509
|
-
/* Split button
|
|
1509
|
+
/* Split button, main action + attached menu toggle (caret) */
|
|
1510
1510
|
.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);}
|
|
1511
1511
|
/* One control: the two halves must stay coplanar. Neutralize each segment's own
|
|
1512
1512
|
lift + drop-shadow (they would misalign the seam / glow a single half); the
|
|
@@ -1521,7 +1521,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1521
1521
|
.fdy-btn-split__toggle:hover,.fdy-btn-split__toggle:focus-visible{z-index:1;}
|
|
1522
1522
|
.fdy-btn-split__toggle svg{display:block;width:1rem;height:1rem;}
|
|
1523
1523
|
|
|
1524
|
-
/* Freeday
|
|
1524
|
+
/* Freeday. Modal (native <dialog>) */
|
|
1525
1525
|
.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;}
|
|
1526
1526
|
.fdy-modal--sm{width:min(24rem,calc(100vw - var(--space-8)));}
|
|
1527
1527
|
.fdy-modal--md{width:min(32rem,calc(100vw - var(--space-8)));}
|
|
@@ -1544,7 +1544,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1544
1544
|
.fdy-modal[open]::backdrop{opacity:0;}
|
|
1545
1545
|
}
|
|
1546
1546
|
|
|
1547
|
-
/* Freeday
|
|
1547
|
+
/* Freeday. Pagination */
|
|
1548
1548
|
.fdy-pagination__list{display:flex;align-items:center;gap:var(--space-1);list-style:none;margin:0;padding:0;}
|
|
1549
1549
|
.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);}
|
|
1550
1550
|
.fdy-pagination__link:hover{background:var(--color-surface-2);border-color:var(--color-border);}
|
|
@@ -1553,7 +1553,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1553
1553
|
.fdy-pagination__link[aria-disabled="true"],.fdy-pagination__link:disabled{opacity:.5;cursor:not-allowed;pointer-events:none;}
|
|
1554
1554
|
.fdy-pagination__ellipsis{display:inline-flex;align-items:center;justify-content:center;min-width:2.25rem;height:2.25rem;color:var(--color-text-subtle);}
|
|
1555
1555
|
|
|
1556
|
-
/* Freeday
|
|
1556
|
+
/* Freeday. Progress */
|
|
1557
1557
|
.fdy-progress{height:.5rem;border-radius:var(--radius-full);background:var(--color-surface-3);overflow:hidden;}
|
|
1558
1558
|
.fdy-progress__bar{height:100%;border-radius:inherit;background:var(--color-primary);transition:width var(--dur-base) var(--ease-standard);}
|
|
1559
1559
|
.fdy-progress--indeterminate{position:relative;}
|
|
@@ -1561,7 +1561,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1561
1561
|
@keyframes fdy-progress-indet{0%{margin-left:-40%;}100%{margin-left:100%;}}
|
|
1562
1562
|
@media (prefers-reduced-motion:reduce){.fdy-progress--indeterminate .fdy-progress__bar{animation:none;width:100%;opacity:.5;}}
|
|
1563
1563
|
|
|
1564
|
-
/* Freeday
|
|
1564
|
+
/* Freeday. Rating (star rating). Interactive = native radio group (arrow keys, form-assoc);
|
|
1565
1565
|
* filled vs empty differ by fill-vs-outline (not colour alone). Enhanced by freeday-rating.js. */
|
|
1566
1566
|
.fdy-rating{display:inline-flex;gap:var(--space-1);}
|
|
1567
1567
|
.fdy-rating__star{display:inline-flex;line-height:0;cursor:pointer;}
|
|
@@ -1574,17 +1574,17 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1574
1574
|
.fdy-rating--disabled .fdy-rating__star{cursor:not-allowed;}
|
|
1575
1575
|
.fdy-rating--disabled .fdy-rating__star svg{opacity:.5;}
|
|
1576
1576
|
/* Invalid: tint the (outline) empty stars danger so a required-but-empty rating reads as an error,
|
|
1577
|
-
paired with a help/error message
|
|
1577
|
+
paired with a help/error message, never colour alone. */
|
|
1578
1578
|
.fdy-rating--error .fdy-rating__star svg{stroke:var(--color-danger);}
|
|
1579
1579
|
.fdy-rating--sm .fdy-rating__star svg{width:1.1rem;height:1.1rem;}
|
|
1580
1580
|
|
|
1581
|
-
/* Freeday
|
|
1581
|
+
/* Freeday. Selection controls */
|
|
1582
1582
|
/* `flex:none` on every box, track and standalone checkbox is load-bearing, not tidiness.
|
|
1583
1583
|
`.fdy-check` is an inline-flex row of [box, label], and a flex item with a WIDTH is still
|
|
1584
|
-
shrinkable
|
|
1584
|
+
shrinkable, so as soon as the label wrapped to a second line the box gave up horizontal space
|
|
1585
1585
|
to it and rendered an 18px-tall rectangle 13px wide. Squashed, off-square, and different from
|
|
1586
1586
|
every sibling whose label happened to be short: a form of five checkboxes showed three sizes.
|
|
1587
|
-
Nothing in the declarations looked wrong, because the width was never overridden
|
|
1587
|
+
Nothing in the declarations looked wrong, because the width was never overridden, it was
|
|
1588
1588
|
negotiated away. */
|
|
1589
1589
|
.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;}
|
|
1590
1590
|
.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);}
|
|
@@ -1602,7 +1602,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1602
1602
|
.fdy-switch input:checked{background:var(--color-primary);}
|
|
1603
1603
|
.fdy-switch input:checked::before{transform:translateX(calc(var(--control-switch-w) - var(--control-switch-h)));}
|
|
1604
1604
|
|
|
1605
|
-
/* Standalone checkbox (e.g. table row/select-all)
|
|
1605
|
+
/* Standalone checkbox (e.g. table row/select-all), supports :indeterminate */
|
|
1606
1606
|
.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);}
|
|
1607
1607
|
.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%);}
|
|
1608
1608
|
.fdy-checkbox:checked,.fdy-checkbox:indeterminate{background:var(--color-primary);border-color:var(--color-primary);}
|
|
@@ -1611,7 +1611,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1611
1611
|
.fdy-checkbox:focus-visible{outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary) 26%,transparent);}
|
|
1612
1612
|
.fdy-checkbox:disabled{opacity:.5;cursor:not-allowed;}
|
|
1613
1613
|
|
|
1614
|
-
/* Freeday
|
|
1614
|
+
/* Freeday. Skeleton */
|
|
1615
1615
|
.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;}
|
|
1616
1616
|
.fdy-skeleton--text{height:.8rem;margin:.3rem 0;}
|
|
1617
1617
|
.fdy-skeleton--title{height:1.2rem;width:40%;}
|
|
@@ -1624,7 +1624,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1624
1624
|
@keyframes fdy-skeleton{to{background-position:-200% 0;}}
|
|
1625
1625
|
@media (prefers-reduced-motion:reduce){.fdy-skeleton{animation:none;}}
|
|
1626
1626
|
|
|
1627
|
-
/* Freeday
|
|
1627
|
+
/* Freeday. Slider (native <input type="range"> themed via accent-color: keeps native
|
|
1628
1628
|
* keyboard + ARIA, filled track and thumb come free). Optional value output + scale.
|
|
1629
1629
|
* freeday-slider.js just mirrors the value into an <output for="…">. */
|
|
1630
1630
|
.fdy-slider{width:100%;max-width:22rem;height:1.5rem;accent-color:var(--color-primary);cursor:pointer;}
|
|
@@ -1637,14 +1637,14 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1637
1637
|
.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;}
|
|
1638
1638
|
.fdy-slider__scale{display:flex;justify-content:space-between;font-size:var(--text-xs);color:var(--color-text-muted);font-variant-numeric:tabular-nums;}
|
|
1639
1639
|
|
|
1640
|
-
/* Freeday
|
|
1640
|
+
/* Freeday. Spinner */
|
|
1641
1641
|
.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;}
|
|
1642
1642
|
.fdy-spinner--sm{width:1rem;height:1rem;border-width:2px;}
|
|
1643
1643
|
.fdy-spinner--lg{width:2.5rem;height:2.5rem;border-width:3px;}
|
|
1644
1644
|
@keyframes fdy-spin{to{transform:rotate(360deg);}}
|
|
1645
1645
|
@media (prefers-reduced-motion:reduce){.fdy-spinner{animation-duration:2s;}}
|
|
1646
1646
|
|
|
1647
|
-
/* Freeday
|
|
1647
|
+
/* Freeday. States (empty / loading / error placeholders) */
|
|
1648
1648
|
.fdy-state{display:flex;flex-direction:column;align-items:center;text-align:center;gap:var(--space-2);padding:var(--space-10) var(--space-6);}
|
|
1649
1649
|
.fdy-state__icon{display:inline-flex;align-items:center;justify-content:center;width:2.5rem;height:2.5rem;color:var(--color-text-subtle);}
|
|
1650
1650
|
.fdy-state__icon svg{width:100%;height:100%;}
|
|
@@ -1653,7 +1653,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1653
1653
|
.fdy-state__actions{margin-top:var(--space-2);display:flex;gap:var(--space-2);flex-wrap:wrap;justify-content:center;}
|
|
1654
1654
|
.fdy-state--danger .fdy-state__icon{color:var(--color-danger-strong);}
|
|
1655
1655
|
|
|
1656
|
-
/* Freeday
|
|
1656
|
+
/* Freeday. Stepper / Wizard (progress header + optional panels).
|
|
1657
1657
|
* Enhanced by freeday-stepper.js for prev/next navigation. */
|
|
1658
1658
|
.fdy-stepper{display:flex;list-style:none;margin:0;padding:0;}
|
|
1659
1659
|
.fdy-step{flex:1;position:relative;display:flex;flex-direction:column;align-items:center;text-align:center;min-width:0;}
|
|
@@ -1663,7 +1663,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1663
1663
|
.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%;}
|
|
1664
1664
|
.fdy-step__btn:disabled{cursor:default;}
|
|
1665
1665
|
/* The marker carries the lift itself, NOT `__btn`. The connector above is position:absolute with
|
|
1666
|
-
z-index:0, and a positioned box paints after in-flow inline content in the same stacking context
|
|
1666
|
+
z-index:0, and a positioned box paints after in-flow inline content in the same stacking context,
|
|
1667
1667
|
so a marker that is merely inline (an indicator-only stepper with no `__btn`) gets painted THROUGH
|
|
1668
1668
|
by the connector line. Lifting the part that must always exist makes that assembly impossible;
|
|
1669
1669
|
`__btn`'s own z-index stays, harmlessly, for the navigable case. */
|
|
@@ -1680,8 +1680,8 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1680
1680
|
.fdy-step-panels{margin-top:var(--space-5);}
|
|
1681
1681
|
.fdy-step-nav{display:flex;justify-content:space-between;gap:var(--space-2);margin-top:var(--space-5);}
|
|
1682
1682
|
|
|
1683
|
-
/* Freeday
|
|
1684
|
-
/* position:relative on every scroller/clipper
|
|
1683
|
+
/* Freeday. Table */
|
|
1684
|
+
/* position:relative on every scroller/clipper, it makes the box the containing block for its own
|
|
1685
1685
|
absolutely positioned content (.fdy-visually-hidden above all), which `overflow` alone does NOT
|
|
1686
1686
|
clip. Without it a hidden label in a wide table scrolls the whole page. See base.css. */
|
|
1687
1687
|
.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);}
|
|
@@ -1692,7 +1692,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1692
1692
|
.fdy-table tbody tr:last-child td{border-bottom:0;}
|
|
1693
1693
|
.fdy-table tbody tr{transition:background-color var(--dur-fast) var(--ease-standard);}
|
|
1694
1694
|
.fdy-table tbody tr:hover{background:var(--color-surface-2);}
|
|
1695
|
-
/* Monospace data
|
|
1695
|
+
/* Monospace data, ids, codes, IPs, timestamps. Alignment-neutral so it works in any cell
|
|
1696
1696
|
or inline (`<span class="fdy-mono">`), not just numeric columns. */
|
|
1697
1697
|
.fdy-mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums;}
|
|
1698
1698
|
/* Numeric cells are mono + right-aligned. Kept self-contained (not composed from .fdy-mono)
|
|
@@ -1719,7 +1719,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1719
1719
|
.fdy-table__selcol{width:1px;white-space:nowrap;}
|
|
1720
1720
|
.fdy-table tbody tr[aria-selected="true"],.fdy-table tbody tr[aria-selected="true"]:hover{background:var(--color-primary-soft);}
|
|
1721
1721
|
|
|
1722
|
-
/* Frozen header row and/or first column
|
|
1722
|
+
/* Frozen header row and/or first column, a rate matrix, a timetable, any grid read
|
|
1723
1723
|
against two axes at once.
|
|
1724
1724
|
|
|
1725
1725
|
This modifier exists because `position:sticky` alone is NOT enough. With
|
|
@@ -1733,12 +1733,12 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1733
1733
|
/* Freezing the HEADER ROW is its own modifier because it needs a vertical
|
|
1734
1734
|
scrollport (`.fdy-table-scroll--frozen`). A table that only freezes columns
|
|
1735
1735
|
scrolls with the PAGE, and `top:0` against the page sticks the header under
|
|
1736
|
-
the viewport edge
|
|
1736
|
+
the viewport edge, over the app's own top bar, which is worse than not
|
|
1737
1737
|
freezing it. Ask for the axis you actually have. */
|
|
1738
1738
|
.fdy-table--sticky-head thead th{position:sticky;top:0;z-index:2;border-bottom:var(--bw) solid var(--color-border);}
|
|
1739
1739
|
|
|
1740
1740
|
/* Freezing a COLUMN: put `.fdy-table__freeze` on every cell of it, header
|
|
1741
|
-
included, and give each frozen column its own `--fdy-freeze-left
|
|
1741
|
+
included, and give each frozen column its own `--fdy-freeze-left`. 0 for the
|
|
1742
1742
|
first, the summed widths of the frozen columns left of it after that. One
|
|
1743
1743
|
frozen column needs no variable at all. Mark the last one `--edge` so the
|
|
1744
1744
|
reader can see where the frozen block ends.
|
|
@@ -1747,12 +1747,12 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1747
1747
|
widths, which CSS cannot sum, and hard-coding them here would only work for
|
|
1748
1748
|
tables whose columns are all the same width. */
|
|
1749
1749
|
.fdy-table--sticky .fdy-table__freeze{position:sticky;left:var(--fdy-freeze-left,0);}
|
|
1750
|
-
/* A frozen body cell slides OVER the data, so it must be OPAQUE
|
|
1750
|
+
/* A frozen body cell slides OVER the data, so it must be OPAQUE, otherwise the
|
|
1751
1751
|
cells beneath show through it. A `th` already carries the header tint from the
|
|
1752
1752
|
base rule, and that tint is usually the whole point: it is what marks a column
|
|
1753
1753
|
as identity rather than data. So only a `td` is given a surface here. A table
|
|
1754
|
-
that wants its frozen `th` to read as a body row instead
|
|
1755
|
-
a hovered row should band unbroken across the freeze line
|
|
1754
|
+
that wants its frozen `th` to read as a body row instead, a rate matrix, where
|
|
1755
|
+
a hovered row should band unbroken across the freeze line, says so itself. */
|
|
1756
1756
|
.fdy-table--sticky tbody .fdy-table__freeze{z-index:1;}
|
|
1757
1757
|
.fdy-table--sticky tbody td.fdy-table__freeze{background:var(--color-surface);}
|
|
1758
1758
|
.fdy-table--sticky tbody tr:hover td.fdy-table__freeze{background:var(--color-surface-2);}
|
|
@@ -1775,19 +1775,19 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1775
1775
|
.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);}
|
|
1776
1776
|
.fdy-table-footer__info{font-size:var(--text-sm);color:var(--color-text-muted);}
|
|
1777
1777
|
/* Rows per page. `margin-left:auto` eats the free space, so the size control and the pager travel
|
|
1778
|
-
together on the right while the range stays left
|
|
1778
|
+
together on the right while the range stays left, no wrapper element, which is what keeps the
|
|
1779
1779
|
footer's DOM byte-identical for the tables that don't offer one (#008). The control is the kit's
|
|
1780
1780
|
own `.fdy-combo`, never a native <select>: an OS menu is unthemeable, and on macOS it drops a
|
|
1781
|
-
dark panel into a light page
|
|
1781
|
+
dark panel into a light page, which is the reason FdyCombo exists at all. Sized to its content:
|
|
1782
1782
|
`.fdy-combo` is display:block at max-width 22rem, which in a footer is a bar-wide box for two
|
|
1783
|
-
digits. Its HEIGHT is left alone on purpose
|
|
1783
|
+
digits. Its HEIGHT is left alone on purpose, `--control-h`, the same as every other control on
|
|
1784
1784
|
the page. It briefly carried 2.25rem to match the pager links beside it, which made the one
|
|
1785
1785
|
combobox in a compact app 4px taller than all the others; a pager link is a nav button, and the
|
|
1786
1786
|
thing this must agree with is the form controls. */
|
|
1787
1787
|
.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);}
|
|
1788
1788
|
.fdy-table-footer__size .fdy-combo{width:5.5rem;}
|
|
1789
1789
|
|
|
1790
|
-
/* Column filter
|
|
1790
|
+
/* Column filter, funnel button injected (enhancer) or rendered (FdyTable) into a filterable <th> */
|
|
1791
1791
|
.fdy-table__filterwrap{display:inline-flex;align-items:center;vertical-align:middle;}
|
|
1792
1792
|
.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;}
|
|
1793
1793
|
.fdy-table__filterbtn:hover{background:var(--color-surface-3);color:var(--color-text);}
|
|
@@ -1804,7 +1804,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1804
1804
|
.fdy-filter__range .fdy-input{width:6rem;}
|
|
1805
1805
|
.fdy-filter__foot{display:flex;justify-content:space-between;gap:var(--space-2);margin-top:var(--space-3);}
|
|
1806
1806
|
|
|
1807
|
-
/* Bulk-action bar
|
|
1807
|
+
/* Bulk-action bar, appears above the table body once rows are selected */
|
|
1808
1808
|
.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);}
|
|
1809
1809
|
.fdy-table-bulkbar[hidden]{display:none;}
|
|
1810
1810
|
.fdy-table-bulkbar__count{font-weight:var(--weight-semibold);white-space:nowrap;}
|
|
@@ -1812,14 +1812,14 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1812
1812
|
.fdy-table-bulkbar__actions{display:flex;gap:var(--space-2);}
|
|
1813
1813
|
|
|
1814
1814
|
/* Controls inside a data row. `.fdy-input` and friends are width:100%, which in an auto-layout
|
|
1815
|
-
table means they contribute NO intrinsic width
|
|
1815
|
+
table means they contribute NO intrinsic width, the column shrinks to whatever the header text
|
|
1816
1816
|
needs and the control collapses with it. A NOTE column ends up a box too narrow to read what you
|
|
1817
1817
|
typed into it. The floor is per-control rather than on the column, because the table does not
|
|
1818
1818
|
know which of its columns hold controls. */
|
|
1819
1819
|
.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;}
|
|
1820
1820
|
|
|
1821
|
-
/* Freeday
|
|
1822
|
-
/* position:relative
|
|
1821
|
+
/* Freeday. Tabs (WAI-ARIA APG) */
|
|
1822
|
+
/* position:relative, containing block for out-of-flow content in the tabs (see base.css). */
|
|
1823
1823
|
.fdy-tabs__list{position:relative;display:flex;gap:var(--space-1);border-bottom:var(--bw) solid var(--color-border);overflow-x:auto;}
|
|
1824
1824
|
.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);}
|
|
1825
1825
|
.fdy-tabs__tab:hover{color:var(--color-text);}
|
|
@@ -1827,7 +1827,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1827
1827
|
.fdy-tabs__tab:disabled:hover,.fdy-tabs__tab[aria-disabled="true"]:hover{color:var(--color-text-muted);}
|
|
1828
1828
|
/* `aria-current="page"` is honoured alongside `aria-selected="true"` so the tab look can be used for
|
|
1829
1829
|
ROUTE-driven sub-navigation (/settings/profile · /settings/billing), where the tabs are real links.
|
|
1830
|
-
A link cannot be `aria-selected
|
|
1830
|
+
A link cannot be `aria-selected`, that is invalid ARIA on an anchor; `aria-current` is the only
|
|
1831
1831
|
correct marker, and it is what a router sets. Use `.fdy-tabs` + `freeday-tabs.js` (roving tabindex,
|
|
1832
1832
|
a panel per tab) for in-page tabs; use these classes on plain links for routed ones, and
|
|
1833
1833
|
`.fdy-nav--horizontal` for an application's PRIMARY navigation. */
|
|
@@ -1838,7 +1838,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1838
1838
|
/* Reveal the newly-shown panel on tab switch (fade + rise), reduced-motion gated. */
|
|
1839
1839
|
@media (prefers-reduced-motion:no-preference){.fdy-tabs__panel:not([hidden]){animation:fdy-combo-in var(--dur-base) var(--ease-standard);}}
|
|
1840
1840
|
|
|
1841
|
-
/* Freeday
|
|
1841
|
+
/* Freeday. Timeline (vertical event feed: dot markers on a connector line). Pure CSS. */
|
|
1842
1842
|
.fdy-timeline{list-style:none;margin:0;padding:0;}
|
|
1843
1843
|
.fdy-timeline__item{position:relative;padding:0 0 var(--space-5) var(--space-6);border-left:2px solid var(--color-border);}
|
|
1844
1844
|
.fdy-timeline__item:last-child{border-left-color:transparent;padding-bottom:0;}
|
|
@@ -1851,7 +1851,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1851
1851
|
.fdy-timeline__title{margin:0;font-size:var(--text-sm);font-weight:var(--weight-semibold);color:var(--color-text);}
|
|
1852
1852
|
.fdy-timeline__text{margin:var(--space-1) 0 0;font-size:var(--text-sm);color:var(--color-text-muted);line-height:var(--leading-normal);}
|
|
1853
1853
|
|
|
1854
|
-
/* Freeday
|
|
1854
|
+
/* Freeday. Time picker (input-styled trigger + scrollable time-list popover).
|
|
1855
1855
|
* Built by freeday-timepicker.js from an empty [data-fdy-timepicker] wrapper.
|
|
1856
1856
|
* Trigger visual mirrors the date picker for a consistent control family. */
|
|
1857
1857
|
.fdy-timepicker{position:relative;display:inline-block;width:100%;max-width:11rem;}
|
|
@@ -1875,14 +1875,14 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1875
1875
|
.fdy-timepicker__opt[aria-selected="true"]{background:var(--color-primary-soft);color:var(--color-primary-strong);font-weight:var(--weight-semibold);}
|
|
1876
1876
|
@media (prefers-reduced-motion:no-preference){.fdy-timepicker__panel:not([hidden]){animation:fdy-combo-in var(--dur-fast) var(--ease-standard);}}
|
|
1877
1877
|
|
|
1878
|
-
/* Freeday
|
|
1878
|
+
/* Freeday. Toast (transient notification) */
|
|
1879
1879
|
/* The region is a POPOVER, not merely a high z-index.
|
|
1880
|
-
`.fdy-modal` is a native <dialog> opened with showModal(), which puts it in the TOP LAYER
|
|
1880
|
+
`.fdy-modal` is a native <dialog> opened with showModal(), which puts it in the TOP LAYER, a
|
|
1881
1881
|
layer above the whole z-index universe, so no number here could ever beat it. A toast raised by
|
|
1882
1882
|
an action taken inside a modal was painted behind that modal's backdrop: dimmed, unreadable, and
|
|
1883
1883
|
most of it under the dialog itself. `freeday-toast.js` calls showPopover() as each toast is
|
|
1884
1884
|
added, which puts the region in the top layer too, and last-in wins there.
|
|
1885
|
-
Everything after `pointer-events` is undoing the UA's [popover] defaults
|
|
1885
|
+
Everything after `pointer-events` is undoing the UA's [popover] defaults, it arrives with
|
|
1886
1886
|
inset:0, auto margins, a border, padding, a Canvas background and overflow:auto, none of which a
|
|
1887
1887
|
toast stack wants. Where showPopover() is unavailable the element is still an ordinary fixed
|
|
1888
1888
|
block at z-index 200, which is exactly what it was before. */
|
|
@@ -1905,14 +1905,14 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1905
1905
|
@media (prefers-reduced-motion:no-preference){.fdy-toast{animation:fdy-toast-in var(--dur-base) var(--ease-spring);}}
|
|
1906
1906
|
@keyframes fdy-toast-in{from{opacity:0;transform:translateY(14px);}to{opacity:1;transform:none;}}
|
|
1907
1907
|
|
|
1908
|
-
/* Freeday
|
|
1908
|
+
/* Freeday. Tooltip (CSS-only, hover/focus) */
|
|
1909
1909
|
.fdy-tooltip-wrap{position:relative;display:inline-flex;}
|
|
1910
1910
|
.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);}
|
|
1911
1911
|
.fdy-tooltip::after{content:"";position:absolute;top:100%;left:50%;transform:translateX(-50%);border:.3rem solid transparent;border-top-color:var(--color-inverse-surface);}
|
|
1912
1912
|
.fdy-tooltip-wrap:hover .fdy-tooltip,.fdy-tooltip-wrap:focus-within .fdy-tooltip{opacity:1;visibility:visible;transform:translateX(-50%) translateY(0);}
|
|
1913
1913
|
@media (prefers-reduced-motion:reduce){.fdy-tooltip{transition:none;transform:translateX(-50%);}}
|
|
1914
1914
|
|
|
1915
|
-
/* Freeday
|
|
1915
|
+
/* Freeday. Tree view (hierarchy via nested native <details>: keyboard-toggle, zero-JS).
|
|
1916
1916
|
* Branches expand/collapse; leaves are plain rows. Chevron + optional folder/file icons. */
|
|
1917
1917
|
.fdy-tree{list-style:none;margin:0;padding:0;font-size:var(--text-sm);}
|
|
1918
1918
|
.fdy-tree ul{list-style:none;margin:0;padding-left:var(--space-4);}
|
|
@@ -1929,7 +1929,7 @@ fieldset.fdy-field>legend{padding:0;float:none;margin-bottom:var(--space-2);}
|
|
|
1929
1929
|
/* Disabled row (branch summary or leaf): dimmed, no hover, not interactive. */
|
|
1930
1930
|
.fdy-tree__leaf[aria-disabled="true"],.fdy-tree__branch[aria-disabled="true"] > summary{opacity:.5;cursor:not-allowed;pointer-events:none;}
|
|
1931
1931
|
|
|
1932
|
-
/* Checkbox variant
|
|
1932
|
+
/* Checkbox variant, a selectable tree (cascading parent/child via freeday-tree.js).
|
|
1933
1933
|
* A native <input class="fdy-checkbox"> sits at the start of every row; a partially
|
|
1934
1934
|
* selected branch shows the native :indeterminate dash. The checkbox stops click
|
|
1935
1935
|
* propagation, so toggling selection never expands/collapses the branch. */
|