@apliteni/apliteni-ui 0.124.0 → 0.124.2
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/guidelines/colour-and-theming.md +12 -0
- package/package.json +1 -1
- package/src/components/dropdown.js +30 -6
- package/src/styles/command-palette.css +17 -0
- package/src/styles/layout.css +39 -1
- package/src/styles/nav.css +61 -0
- package/src/styles/pagination.css +19 -0
- package/src/styles/segmented.css +25 -5
- package/src/styles/topbar.css +1 -1
|
@@ -70,3 +70,15 @@
|
|
|
70
70
|
**Except:** The sidebar rail's current row. Its mark may be neither an accent nor an edge — an outline there means focus — and in the page shell the rail is the card surface already. It takes the quiet fill a rung off its rail, label and all. Artur, r34 of [#475](https://github.com/apliteni/apliteni-ui/issues/475).
|
|
71
71
|
|
|
72
72
|
**Except:** A status chip carries a soft fill. Where its tone reports no verdict it takes a light tint of muted ink that leaves the panel in one direction per theme, so the neutral tones read as the same family as the coloured ones.
|
|
73
|
+
|
|
74
|
+
## Mark selection in forced colours
|
|
75
|
+
|
|
76
|
+
<!-- rule: selected-forced-colours -->
|
|
77
|
+
|
|
78
|
+
**Rule:** In forced colours, fill a selected item with the highlight colour and its label with the highlight text colour.
|
|
79
|
+
|
|
80
|
+
**Why:** The mode replaces author backgrounds. A specimen cannot switch it on, so this rule draws no pair.
|
|
81
|
+
|
|
82
|
+
**Do:** Ring a focused selected item in the page text colour.
|
|
83
|
+
|
|
84
|
+
**Don't:** Mark selection with an outline.
|
package/package.json
CHANGED
|
@@ -637,7 +637,10 @@ function ddSizeHeight(dd, panel) {
|
|
|
637
637
|
/** Fit a panel again where it now is, whichever placement it uses. */
|
|
638
638
|
function ddPlace(dd) {
|
|
639
639
|
const panel = dd.__ddPanel || ddPanelOf(dd);
|
|
640
|
-
if (dd.__ddPanel)
|
|
640
|
+
if (dd.__ddPanel) {
|
|
641
|
+
ddResolveDirection(dd, panel);
|
|
642
|
+
positionPortalPanel(dd, panel);
|
|
643
|
+
}
|
|
641
644
|
else ddFitViewport(dd, ddPanelOf(dd));
|
|
642
645
|
ddSizeHeight(dd, panel);
|
|
643
646
|
}
|
|
@@ -700,16 +703,36 @@ function ddCancelClose(dd, panel) {
|
|
|
700
703
|
else panel?.classList?.remove('is-closing');
|
|
701
704
|
}
|
|
702
705
|
|
|
703
|
-
//
|
|
704
|
-
// panel's own class, set once at render. Flip only when below is too tight AND
|
|
705
|
-
// above is roomier, so a panel that fits nowhere still opens the way it says.
|
|
706
|
+
// Keep the configured side separate from the side resolved for this opening.
|
|
706
707
|
function ddResolveDirection(dd, panel) {
|
|
707
|
-
|
|
708
|
+
const auto = dd.getAttribute('data-dropdown-direction') === 'auto';
|
|
709
|
+
if (!auto && !dd.__ddPanel) return;
|
|
708
710
|
const trigger = dd.querySelector('[data-dropdown-trigger]');
|
|
709
711
|
if (!trigger || typeof trigger.getBoundingClientRect !== 'function') return;
|
|
710
712
|
const t = trigger.getBoundingClientRect();
|
|
711
713
|
const below = ddViewOf(panel).innerHeight - t.bottom;
|
|
712
|
-
|
|
714
|
+
if (auto) {
|
|
715
|
+
ddSetUp(panel, below < panel.offsetHeight + ddGap(panel) && t.top > below);
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
const up = dd.__ddPreferredUp;
|
|
719
|
+
const room = (up ? t.top : below) - ddGap(panel);
|
|
720
|
+
const opposite = (up ? below : t.top) - ddGap(panel);
|
|
721
|
+
// Short content or a smaller consumer cap needs less than the usual floor.
|
|
722
|
+
const needed = Math.min(ddMin(panel), panel.offsetHeight);
|
|
723
|
+
ddSetUp(panel, room < needed && opposite > room ? !up : up);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/* Change the side without a transition: the shut panel's transform would otherwise
|
|
727
|
+
* be running when `is-open` arrives, and the open would retarget it from the wrong
|
|
728
|
+
* side. */
|
|
729
|
+
function ddSetUp(panel, up) {
|
|
730
|
+
if (panel.classList.contains('is-up') === up) return;
|
|
731
|
+
const held = panel.style.transition;
|
|
732
|
+
panel.style.transition = 'none';
|
|
733
|
+
panel.classList.toggle('is-up', up);
|
|
734
|
+
ddViewOf(panel).getComputedStyle(panel).transform;
|
|
735
|
+
panel.style.transition = held;
|
|
713
736
|
}
|
|
714
737
|
|
|
715
738
|
/** The shift a panel is already carrying, the one ddFitViewport() wrote. Taken
|
|
@@ -1057,6 +1080,7 @@ export function wireDropdown(root = document) {
|
|
|
1057
1080
|
sweepOrphanPanels(host);
|
|
1058
1081
|
panel.setAttribute('data-dropdown-portal', '');
|
|
1059
1082
|
panel.__ddOwner = dd;
|
|
1083
|
+
dd.__ddPreferredUp = panel.classList.contains('is-up');
|
|
1060
1084
|
dd.__ddPanel = panel;
|
|
1061
1085
|
host.appendChild(panel);
|
|
1062
1086
|
if (dd.classList.contains('open')) {
|
|
@@ -340,3 +340,20 @@
|
|
|
340
340
|
@media (prefers-reduced-motion: reduce) {
|
|
341
341
|
.ui-cmdk.is-open * { transition: none !important; }
|
|
342
342
|
}
|
|
343
|
+
|
|
344
|
+
/* Forced colours replace every author background, so a selected item takes the system
|
|
345
|
+
pair as its fill. `forced-color-adjust: none` is what keeps the pair from sitting on
|
|
346
|
+
Chromium's Canvas backplate, which would leave the label unreadable. Focus keeps its
|
|
347
|
+
own ring, drawn in CanvasText so the fill is never mistaken for it. */
|
|
348
|
+
@media (forced-colors: active) {
|
|
349
|
+
.ui-cmdk__item.is-active { forced-color-adjust: none; background: Highlight !important; color: HighlightText !important; --ring: var(--ring-width) solid CanvasText; }
|
|
350
|
+
.ui-cmdk__item.is-active .ui-cmdk__label,
|
|
351
|
+
.ui-cmdk__item.is-active .ui-cmdk__ic,
|
|
352
|
+
.ui-cmdk__item.is-active .ui-cmdk__desc,
|
|
353
|
+
.ui-cmdk__item.is-danger.is-active .ui-cmdk__label,
|
|
354
|
+
.ui-cmdk__item.is-danger.is-active .ui-cmdk__ic { color: HighlightText; }
|
|
355
|
+
.ui-cmdk--roomy .ui-cmdk__item.is-active .ui-cmdk__ic { background: none; }
|
|
356
|
+
.ui-cmdk__item.is-active .ui-cmdk__badge { background: HighlightText; color: Highlight; }
|
|
357
|
+
/* Shortcut caps inherit the adjust, so they take the pair too: the item's ink on its fill, with an ink edge. */
|
|
358
|
+
.ui-cmdk__item.is-active .ui-cmdk__key { background: Highlight; color: HighlightText; border-color: HighlightText; }
|
|
359
|
+
}
|
package/src/styles/layout.css
CHANGED
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
resting step. */
|
|
120
120
|
/* Re-tuned at #295, which took the light rail down a step with the page. Below 720px
|
|
121
121
|
the glyph is the whole row, so it is held at text grade rather than at 1.4.11's bar —
|
|
122
|
-
stories/apps/shell-states.test.js:
|
|
122
|
+
stories/apps/shell-states.test.js:1708 `a resting glyph is dimmer but still legible`. */
|
|
123
123
|
.ui-app__rail .ui-nav__ic svg { opacity: 0.66; }
|
|
124
124
|
|
|
125
125
|
/* A destructive row of the rail is a navigation row and rests like one; the danger
|
|
@@ -708,3 +708,41 @@
|
|
|
708
708
|
.ui-section-head { text-align: center; max-width: var(--panel-lg); margin: 0 auto clamp(40px, 6vh, 64px); }
|
|
709
709
|
.ui-section-head h2 { color: var(--strong); font-size: clamp(28px, 4vw, 40px); font-weight: 700; letter-spacing: -0.02em; margin-bottom: 14px; }
|
|
710
710
|
.ui-section-head p { color: var(--text); font-size: calc(var(--text-lg) - 1px); line-height: 1.6; }
|
|
711
|
+
|
|
712
|
+
/* Forced colours replace every author background, so a selected item takes the system
|
|
713
|
+
pair as its fill. `forced-color-adjust: none` is what keeps the pair from sitting on
|
|
714
|
+
Chromium's Canvas backplate, which would leave the label unreadable. Focus keeps its
|
|
715
|
+
own ring, drawn in CanvasText so the fill is never mistaken for it. The pair is
|
|
716
|
+
`!important` so the shell's danger ink and every hover or focus state yield to it. */
|
|
717
|
+
@media (forced-colors: active) {
|
|
718
|
+
.ui-app__rail .ui-nav__item:is(.is-active, .is-current):not(.is-disabled) { forced-color-adjust: none; background: Highlight !important; color: HighlightText !important; --ring: var(--ring-width) solid CanvasText; }
|
|
719
|
+
.ui-app__rail .ui-nav__item:is(.is-active, .is-current).is-disabled { forced-color-adjust: none; background: GrayText !important; color: Canvas !important; /* muted-ink: state — unavailable control */ }
|
|
720
|
+
.ui-app__rail .ui-nav__item.is-active:not(.is-disabled)::before { background: Highlight; }
|
|
721
|
+
.ui-app__rail .ui-nav__item.is-active.is-disabled::before { background: GrayText; }
|
|
722
|
+
/* A folded row is only an icon strip: the plate carries the fill, so the row paints none. */
|
|
723
|
+
.ui-app.is-collapsed .ui-app__rail .ui-nav__item:is(.is-active, .is-current) { isolation: isolate; background: none !important; }
|
|
724
|
+
.ui-app.is-collapsed .ui-app__rail .ui-nav__item.is-current:not(.is-active)::before {
|
|
725
|
+
content: ""; position: absolute; left: 0; top: 50%; width: var(--ui-nav-strip); height: 100%; border-radius: 10px;
|
|
726
|
+
transform: translateY(-50%); /* motion: still — a mark on the current group, centred and not travelling */
|
|
727
|
+
z-index: -1;
|
|
728
|
+
}
|
|
729
|
+
.ui-app.is-collapsed .ui-app__rail .ui-nav__item.is-current:not(.is-active):not(.is-disabled)::before { background: Highlight; }
|
|
730
|
+
.ui-app.is-collapsed .ui-app__rail .ui-nav__item.is-current.is-disabled:not(.is-active)::before { background: GrayText; }
|
|
731
|
+
/* The floating name of a selected folded row inherits the row's adjust, so it is painted in system colours. */
|
|
732
|
+
.ui-app.is-collapsed .ui-app__rail .ui-nav__item:is(.is-active, .is-current):is(:hover, :focus-visible) > .ui-nav__label {
|
|
733
|
+
forced-color-adjust: auto; background: Canvas; color: CanvasText; border-color: CanvasText;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
@media (forced-colors: active) and (max-width: 720px) {
|
|
737
|
+
.ui-app .ui-app__rail .ui-nav__item:is(.is-active, .is-current) { isolation: isolate; background: none !important; }
|
|
738
|
+
.ui-app .ui-app__rail .ui-nav__item.is-current:not(.is-active)::before {
|
|
739
|
+
content: ""; position: absolute; left: 0; top: 50%; width: var(--ui-nav-strip); height: 100%; border-radius: 10px;
|
|
740
|
+
transform: translateY(-50%);
|
|
741
|
+
z-index: -1;
|
|
742
|
+
}
|
|
743
|
+
.ui-app .ui-app__rail .ui-nav__item.is-current:not(.is-active):not(.is-disabled)::before { background: Highlight; }
|
|
744
|
+
.ui-app .ui-app__rail .ui-nav__item.is-current.is-disabled:not(.is-active)::before { background: GrayText; }
|
|
745
|
+
.ui-app .ui-app__rail .ui-nav__item:is(.is-active, .is-current):is(:hover, :focus-visible) > .ui-nav__label {
|
|
746
|
+
forced-color-adjust: auto; background: Canvas; color: CanvasText; border-color: CanvasText;
|
|
747
|
+
}
|
|
748
|
+
}
|
package/src/styles/nav.css
CHANGED
|
@@ -365,3 +365,64 @@ a.ui-nav__crumb:hover { color: var(--strong); background: var(--surface); /* cod
|
|
|
365
365
|
a.ui-nav__crumb:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); }
|
|
366
366
|
.ui-nav__crumb.is-current { color: var(--strong); font-weight: 500; }
|
|
367
367
|
.ui-nav__crumb .ui-nav__ic svg { width: 15px; height: 15px; stroke: currentColor; fill: none; stroke-width: 2.5; }
|
|
368
|
+
|
|
369
|
+
/* Forced colours replace every author background, so a selected item takes the system
|
|
370
|
+
pair as its fill. `forced-color-adjust: none` is what keeps the pair from sitting on
|
|
371
|
+
Chromium's Canvas backplate, which would leave the label unreadable. Focus keeps its
|
|
372
|
+
own ring, drawn in CanvasText so the fill is never mistaken for it.
|
|
373
|
+
The pair is `!important` because it is the reader's palette: a danger wash, a hover
|
|
374
|
+
or a focus state must never repaint it. A child of the row inherits the adjust, so
|
|
375
|
+
each child that paints its own colour is repainted here as well. */
|
|
376
|
+
@media (forced-colors: active) {
|
|
377
|
+
.ui-nav__item.is-active:not(.is-disabled),
|
|
378
|
+
.ui-nav__item.is-current:not(.is-disabled),
|
|
379
|
+
.ui-nav__tab.is-active:not(.is-disabled),
|
|
380
|
+
.ui-nav--tabs.is-pill .ui-nav__tab.is-active:not(.is-disabled),
|
|
381
|
+
.ui-nav__crumb.is-current {
|
|
382
|
+
forced-color-adjust: none;
|
|
383
|
+
background: Highlight !important;
|
|
384
|
+
color: HighlightText !important;
|
|
385
|
+
--ring: var(--ring-width) solid CanvasText;
|
|
386
|
+
}
|
|
387
|
+
.ui-nav__item.is-active:not(.is-disabled) .ui-nav__ic svg,
|
|
388
|
+
.ui-nav__item.is-current:not(.is-disabled) .ui-nav__ic svg,
|
|
389
|
+
.ui-nav__tab.is-active:not(.is-disabled) .ui-nav__ic svg,
|
|
390
|
+
.ui-nav__crumb.is-current .ui-nav__ic svg { stroke: HighlightText; }
|
|
391
|
+
.ui-nav__item.is-active .ui-nav__caret,
|
|
392
|
+
.ui-nav__item.is-current .ui-nav__caret { border-color: currentColor; }
|
|
393
|
+
.ui-nav__item.is-active:not(.is-disabled) .ui-nav__badge,
|
|
394
|
+
.ui-nav__item.is-current:not(.is-disabled) .ui-nav__badge,
|
|
395
|
+
.ui-nav__tab.is-active:not(.is-disabled) .ui-nav__badge {
|
|
396
|
+
background: HighlightText !important;
|
|
397
|
+
color: Highlight !important;
|
|
398
|
+
}
|
|
399
|
+
/* A selected item that is unavailable must not look live, so it takes the disabled system pair. */
|
|
400
|
+
.ui-nav__item.is-active.is-disabled,
|
|
401
|
+
.ui-nav__item.is-current.is-disabled,
|
|
402
|
+
.ui-nav__tab.is-active.is-disabled {
|
|
403
|
+
forced-color-adjust: none;
|
|
404
|
+
background: GrayText !important;
|
|
405
|
+
color: Canvas !important; /* muted-ink: state — unavailable control */
|
|
406
|
+
}
|
|
407
|
+
.ui-nav__item.is-active.is-disabled .ui-nav__ic svg,
|
|
408
|
+
.ui-nav__item.is-current.is-disabled .ui-nav__ic svg,
|
|
409
|
+
.ui-nav__tab.is-active.is-disabled .ui-nav__ic svg { stroke: Canvas; }
|
|
410
|
+
.ui-nav__item.is-active.is-disabled .ui-nav__badge,
|
|
411
|
+
.ui-nav__item.is-current.is-disabled .ui-nav__badge,
|
|
412
|
+
.ui-nav__tab.is-active.is-disabled .ui-nav__badge {
|
|
413
|
+
background: Canvas !important;
|
|
414
|
+
color: GrayText !important; /* muted-ink: state — unavailable control */
|
|
415
|
+
}
|
|
416
|
+
/* A folded row is only an icon strip: the plate carries the fill, so the row paints none.
|
|
417
|
+
A current group takes the same plate as the current row it holds. */
|
|
418
|
+
.ui-nav--side.is-collapsed .ui-nav__item:is(.is-active, .is-current) { isolation: isolate; background: none !important; }
|
|
419
|
+
.ui-nav--side.is-collapsed .ui-nav__item.is-current:not(.is-active)::before {
|
|
420
|
+
content: ""; position: absolute; left: 0; top: 50%; width: var(--ui-nav-strip); height: 100%; border-radius: 10px;
|
|
421
|
+
transform: translateY(-50%); /* motion: still — a mark on the current group, centred and not travelling */
|
|
422
|
+
z-index: -1;
|
|
423
|
+
}
|
|
424
|
+
.ui-nav--side.is-collapsed .ui-nav__item:is(.is-active, .is-current):not(.is-disabled)::before { background: Highlight; }
|
|
425
|
+
.ui-nav--side.is-collapsed .ui-nav__item:is(.is-active, .is-current).is-disabled::before { background: GrayText; }
|
|
426
|
+
/* The fill is the one mark, so the underline bar, which would paint the accent inside it, is gone. */
|
|
427
|
+
.ui-nav--tabs.is-underline .ui-nav__tab.is-active::after { display: none; /* motion: still — the bar is not drawn while the fill marks the tab */ }
|
|
428
|
+
}
|
|
@@ -137,3 +137,22 @@ a.ui-pager__step,
|
|
|
137
137
|
a.ui-pager__page { color: var(--text); }
|
|
138
138
|
a.ui-pager__step:hover,
|
|
139
139
|
a.ui-pager__page:hover { color: var(--strong); }
|
|
140
|
+
|
|
141
|
+
/* Forced colours replace every author background, so a selected item takes the system
|
|
142
|
+
pair as its fill. `forced-color-adjust: none` is what keeps the pair from sitting on
|
|
143
|
+
Chromium's Canvas backplate, which would leave the label unreadable. Focus keeps its
|
|
144
|
+
own ring, drawn in CanvasText so the fill is never mistaken for it. */
|
|
145
|
+
@media (forced-colors: active) {
|
|
146
|
+
.ui-pager__page.is-current:not([disabled]) {
|
|
147
|
+
forced-color-adjust: none;
|
|
148
|
+
background: Highlight;
|
|
149
|
+
color: HighlightText;
|
|
150
|
+
--ring: var(--ring-width) solid CanvasText;
|
|
151
|
+
}
|
|
152
|
+
/* A busy pager must not look live, so its current page takes the disabled system colour. */
|
|
153
|
+
.ui-pager__page.is-current[disabled] {
|
|
154
|
+
forced-color-adjust: none;
|
|
155
|
+
background: GrayText;
|
|
156
|
+
color: Canvas; /* muted-ink: state — unavailable control */
|
|
157
|
+
}
|
|
158
|
+
}
|
package/src/styles/segmented.css
CHANGED
|
@@ -153,11 +153,31 @@
|
|
|
153
153
|
}
|
|
154
154
|
.ui-seg--underline button.is-active::before,
|
|
155
155
|
.ui-seg--underline button[aria-pressed="true"]::before { opacity: 1; transform: scaleX(1); }
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
.ui-seg button:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); }
|
|
157
|
+
.ui-seg button:disabled { /* muted-ink: state — unavailable choice */ color: var(--disabled-ink); cursor: not-allowed; }
|
|
158
|
+
|
|
159
|
+
/* Forced colours replace every author background, so a selected item takes the system
|
|
160
|
+
pair as its fill. `forced-color-adjust: none` is what keeps the pair from sitting on
|
|
161
|
+
Chromium's Canvas backplate, which would leave the label unreadable. Focus keeps its
|
|
162
|
+
own ring, drawn in CanvasText so the fill is never mistaken for it. */
|
|
158
163
|
@media (forced-colors: active) {
|
|
164
|
+
.ui-seg button.is-active,
|
|
165
|
+
.ui-seg button[aria-pressed="true"],
|
|
166
|
+
.ui-seg button[aria-selected="true"] {
|
|
167
|
+
forced-color-adjust: none;
|
|
168
|
+
background: Highlight;
|
|
169
|
+
color: HighlightText;
|
|
170
|
+
--ring: var(--ring-width) solid CanvasText;
|
|
171
|
+
}
|
|
172
|
+
/* The fill is the one mark, so the underline bar, which would paint the accent inside it, is gone. */
|
|
159
173
|
.ui-seg--underline button.is-active::before,
|
|
160
|
-
.ui-seg--underline button[aria-pressed="true"]::before
|
|
174
|
+
.ui-seg--underline button[aria-pressed="true"]::before,
|
|
175
|
+
.ui-seg--underline button[aria-selected="true"]::before { display: none; /* motion: still — the bar is not drawn while the fill marks the choice */ }
|
|
176
|
+
/* A disabled selected choice must not look live, so it takes the disabled system colour. */
|
|
177
|
+
.ui-seg button.is-active:disabled,
|
|
178
|
+
.ui-seg button[aria-pressed="true"]:disabled,
|
|
179
|
+
.ui-seg button[aria-selected="true"]:disabled {
|
|
180
|
+
background: GrayText;
|
|
181
|
+
color: Canvas; /* muted-ink: state — unavailable choice */
|
|
182
|
+
}
|
|
161
183
|
}
|
|
162
|
-
.ui-seg button:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); }
|
|
163
|
-
.ui-seg button:disabled { /* muted-ink: state — unavailable choice */ color: var(--disabled-ink); cursor: not-allowed; }
|
package/src/styles/topbar.css
CHANGED
|
@@ -209,7 +209,7 @@
|
|
|
209
209
|
src/styles/dropdown.css:271 `.ui-dropdown__item:hover`.
|
|
210
210
|
why: docs/foundations.md#elevation */
|
|
211
211
|
.vopt:hover { outline: 1px solid var(--control-edge); outline-offset: -1px; background: var(--surface); /* code-bg: inherit — control paint; the containing surface is the ground, for a chip and for a band alike. */ }
|
|
212
|
-
/* The row is a keyboard stop: src/components/dropdown.js:
|
|
212
|
+
/* The row is a keyboard stop: src/components/dropdown.js:1165
|
|
213
213
|
`items[next < 0 ? 0 : next]?.focus()` moves focus onto a `[data-dd-item]` with
|
|
214
214
|
the arrow keys, and this is one. It takes the ring on its own 10px radius, the
|
|
215
215
|
same way `.amenu a` above does. #482 */
|