palace-ui 0.12.0 → 0.12.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/README.md CHANGED
@@ -13,4 +13,10 @@ rotation, chunky offset shadows. One stylesheet, no build step, no JavaScript.
13
13
  - `SPEC.md`: the design specification, written to be followed exactly by people and AI agents alike
14
14
  - `catalog.html`: every component on one page; open it in a browser
15
15
 
16
+ ## Utilities
17
+
18
+ - `.visually-hidden`: text for screen readers only. A scrolling container
19
+ (`overflow-x: auto`) that holds it needs `position: relative`, or the hidden
20
+ text widens the page.
21
+
16
22
  MIT licensed.
package/SPEC.md CHANGED
@@ -157,7 +157,17 @@ A navbar menu for the signed-in user is a `<details class="navbar-menu">`: the s
157
157
  </nav>
158
158
  ```
159
159
 
160
- `.visually-hidden` hides text visually and keeps it for screen readers.
160
+ Group items with `.navbar-menu-group`: a thin rule above, like the one before sign out, and an optional `.navbar-menu-label` as its heading. A setting that is switched on and off is a `button.navbar-menu-setting` with `aria-pressed`: its name sits on the left and a small square switch on the right, pink when pressed. Toggle `aria-pressed` with a script, or submit a form that renders the new state. Use it for on/off settings, not for links.
161
+
162
+ ```html
163
+ <div class="navbar-menu-group" role="group" aria-labelledby="menu-settings">
164
+ <p class="navbar-menu-label" id="menu-settings">Settings</p>
165
+ <button type="button" class="navbar-menu-setting" aria-pressed="true">Notifications</button>
166
+ <button type="button" class="navbar-menu-setting" aria-pressed="false">Sound</button>
167
+ </div>
168
+ ```
169
+
170
+ `.visually-hidden` hides text visually and keeps it for screen readers (see Visually Hidden).
161
171
 
162
172
  With an icon beside the brand name (`.navbar-brand` uses `inline-flex` layout with `gap: 10px`; images/SVGs are auto-sized to 24×24):
163
173
 
@@ -302,7 +312,7 @@ Use `.card-static` instead of `.card` when the card is a container (e.g. auth fo
302
312
 
303
313
  ### Buttons
304
314
 
305
- Size modifiers compose with any color variant:
315
+ Size modifiers compose with any color variant. `.btn` works on `<button>` and on `<a>` alike: each size sets its own line height, so a button and a link button of the same size are the same height (41px default, 28px `.btn-sm`, 51px `.btn-lg` and `.cta`) and line up side by side.
306
316
 
307
317
  ```html
308
318
  <!-- Size modifiers -->
@@ -626,11 +636,27 @@ Inline keyboard key: bordered, uppercase mono, with a small pink offset shadow.
626
636
  <kbd>Ctrl</kbd> + <kbd>Z</kbd>
627
637
  ```
628
638
 
639
+ ### Visually Hidden
640
+
641
+ `.visually-hidden` keeps text for screen readers and draws nothing: a label for an icon-only column, the name of the row an "Edit" button acts on.
642
+
643
+ ```html
644
+ <button class="btn btn-sm btn-outline">Edit<span class="visually-hidden"> Ada</span></button>
645
+ ```
646
+
647
+ It is absolutely positioned where the text would have been, with no `top` or `left`, so it stays where the text is and a screen reader moving to it does not jump the page. That means it still counts toward the width of its containing block. **A scrolling container (`overflow-x: auto`) holding `.visually-hidden` text needs `position: relative`**; without it the hidden text's containing block is the page, and a wide table makes the whole page scroll sideways on a phone.
648
+
649
+ ```html
650
+ <div style="overflow-x: auto; position: relative;">
651
+ <table>…<th><span class="visually-hidden">Actions</span></th>…</table>
652
+ </div>
653
+ ```
654
+
629
655
  ## Accessibility
630
656
 
631
657
  ### Focus Visible
632
658
 
633
- All interactive elements (buttons, links, inputs) have `:focus-visible` styles with a `2px` outline in accent-2 (purple) and `2px` offset. Buttons also get a lift + shadow on focus.
659
+ All interactive elements (buttons, links, inputs, checkboxes, radio buttons, `<summary>`) have `:focus-visible` styles with a `2px` outline in accent-2 (purple) and `2px` offset. Buttons also get a lift + shadow on focus. Text inputs show focus with a purple border and shadow instead. The navbar menu's summary keeps its own cyan ring.
634
660
 
635
661
  ### Reduced Motion
636
662
 
package/catalog.html CHANGED
@@ -361,6 +361,17 @@
361
361
  <button class="btn btn-lg btn-primary">Large</button>
362
362
  </div>
363
363
 
364
+ <h3 style="margin-bottom: 12px;">Buttons and links side by side</h3>
365
+ <p style="margin-bottom: 12px;">A <code>&lt;button&gt;</code> and an <code>&lt;a class="btn"&gt;</code> of the same size are the same height and line up.</p>
366
+ <div data-source id="btn-match" style="display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; margin-bottom: 24px;">
367
+ <button class="btn btn-sm btn-outline">Button</button>
368
+ <a href="#" class="btn btn-sm btn-outline">Link</a>
369
+ <button class="btn btn-primary">Button</button>
370
+ <a href="#" class="btn btn-primary">Link</a>
371
+ <button class="btn btn-lg btn-secondary">Button</button>
372
+ <a href="#" class="btn btn-lg btn-secondary">Link</a>
373
+ </div>
374
+
364
375
  <h3 style="margin-bottom: 12px;">Disabled</h3>
365
376
  <div data-source style="display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 24px;">
366
377
  <button class="btn btn-primary" disabled>Disabled</button>
@@ -462,6 +473,23 @@
462
473
  </div>
463
474
  </div>
464
475
 
476
+ <h3 style="margin-bottom: 12px;">Checkboxes, radio buttons and disclosures</h3>
477
+ <p style="margin-bottom: 12px;">Tab through them: each gets the same focus ring as links and buttons.</p>
478
+ <div data-source id="focus-controls" class="card-static" style="margin-bottom: 16px;">
479
+ <div class="stack-md">
480
+ <label><input type="checkbox" checked> Show lap times</label>
481
+ <fieldset style="border: 0;">
482
+ <legend class="visually-hidden">Direction</legend>
483
+ <label><input type="radio" name="cat-dir" checked> Anticlockwise</label>
484
+ <label><input type="radio" name="cat-dir"> Clockwise</label>
485
+ </fieldset>
486
+ <details>
487
+ <summary>More options</summary>
488
+ <p>Hidden until the summary is opened.</p>
489
+ </details>
490
+ </div>
491
+ </div>
492
+
465
493
  <h3 style="margin-bottom: 12px;">Inline Form</h3>
466
494
  <div data-source style="display: flex; gap: 8px;">
467
495
  <input type="text" placeholder="Add a label" aria-label="Add a label" style="flex: 1;">
@@ -522,6 +550,20 @@
522
550
  </tbody>
523
551
  </table>
524
552
  </div>
553
+
554
+ <h3 style="margin-bottom: 12px; margin-top: 24px;">Scrolling table</h3>
555
+ <p style="margin-bottom: 12px;">A wide table scrolls inside its wrapper. The wrapper is <code>position: relative</code>, so <code>.visually-hidden</code> text in it stays inside it and the page never grows wider.</p>
556
+ <div data-source id="scroll-table" style="overflow-x: auto; position: relative;">
557
+ <table style="min-width: 640px;">
558
+ <thead>
559
+ <tr><th>Driver</th><th>Laps</th><th>Best</th><th>Last</th><th><span class="visually-hidden">Actions</span></th></tr>
560
+ </thead>
561
+ <tbody>
562
+ <tr><td>Ada</td><td>12</td><td>4.812</td><td>4.977</td><td><button class="btn btn-sm btn-outline">Edit<span class="visually-hidden"> Ada</span></button></td></tr>
563
+ <tr><td>Grace</td><td>11</td><td>4.903</td><td>5.120</td><td><button class="btn btn-sm btn-outline">Edit<span class="visually-hidden"> Grace</span></button></td></tr>
564
+ </tbody>
565
+ </table>
566
+ </div>
525
567
  </div>
526
568
 
527
569
  <!-- ════════════════════════════════════════ CODE ════════════════════════════════════════ -->
@@ -794,7 +836,7 @@
794
836
  </div>
795
837
 
796
838
  <h3 style="margin-bottom: 12px;">With a navbar menu (open it)</h3>
797
- <div data-source style="margin-bottom: 24px; padding-bottom: 180px;">
839
+ <div data-source style="margin-bottom: 24px; padding-bottom: 300px;">
798
840
  <nav class="navbar">
799
841
  <a href="#" class="navbar-brand">App Name</a>
800
842
  <div class="navbar-right">
@@ -804,6 +846,11 @@
804
846
  <p class="navbar-menu-label" aria-hidden="true">Your teams</p>
805
847
  <ul aria-label="Your teams"><li><a href="#" aria-current="true">Team A</a></li><li><a href="#">Team B</a></li></ul>
806
848
  <a href="#">Account</a>
849
+ <div class="navbar-menu-group" role="group" aria-labelledby="cat-menu-settings">
850
+ <p class="navbar-menu-label" id="cat-menu-settings">Settings</p>
851
+ <button type="button" class="navbar-menu-setting" aria-pressed="true">Notifications</button>
852
+ <button type="button" class="navbar-menu-setting" aria-pressed="false">Sound</button>
853
+ </div>
807
854
  <form><button type="button">Sign out</button></form>
808
855
  </div>
809
856
  </details>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "palace-ui",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "description": "Retro-technical design system. Hard edges, vibrant gradients, chunky shadows.",
5
5
  "license": "MIT",
6
6
  "repository": {
package/palace.css CHANGED
@@ -114,11 +114,17 @@
114
114
  /* The hidden attribute always hides, even on elements palace gives a display (buttons, .btn). */
115
115
  [hidden] { display: none !important; }
116
116
 
117
- /* Text for screen readers only: off screen, still read. */
117
+ /* Text for screen readers only: not drawn, still read. It is absolutely
118
+ positioned where it would have been, so inside a scrolling container that is
119
+ not positioned it still widens the page: give that container
120
+ position: relative. */
118
121
  .visually-hidden {
119
122
  position: absolute;
120
123
  width: 1px;
121
124
  height: 1px;
125
+ margin: -1px;
126
+ padding: 0;
127
+ border: 0;
122
128
  overflow: hidden;
123
129
  clip-path: inset(50%);
124
130
  white-space: nowrap;
@@ -319,14 +325,23 @@ button, .btn {
319
325
  border: var(--p-border-width) solid;
320
326
  border-radius: var(--p-radius);
321
327
  transition: var(--p-transition);
328
+ /* Only what a button's states change. A size that follows its container
329
+ (font size, padding, width) must never animate, or it passes through
330
+ sizes in between whenever the container settles. */
331
+ transition-property: background-color, border-color, color, box-shadow, transform, outline-color;
322
332
  text-decoration: none;
323
333
  display: inline-block;
334
+ /* One line height for <button> and <a class="btn">, so both are the same
335
+ height: a button does not inherit the body's line height, a link does. */
336
+ line-height: calc(17 / 13);
337
+ text-align: center;
324
338
  }
325
339
 
326
340
  /* Size modifiers */
327
341
  .btn-sm {
328
342
  padding: 6px 14px;
329
343
  font-size: 11px;
344
+ line-height: calc(14 / 11);
330
345
  letter-spacing: 1px;
331
346
  border-width: var(--p-border-thin);
332
347
  }
@@ -334,6 +349,7 @@ button, .btn {
334
349
  .btn-lg {
335
350
  padding: 14px 28px;
336
351
  font-size: 15px;
352
+ line-height: calc(19 / 15);
337
353
  letter-spacing: 1px;
338
354
  }
339
355
 
@@ -397,6 +413,8 @@ button, .btn {
397
413
  padding: 14px 28px;
398
414
  font-family: var(--p-font);
399
415
  font-size: 15px;
416
+ line-height: calc(19 / 15);
417
+ text-align: center;
400
418
  font-weight: 700;
401
419
  text-transform: uppercase;
402
420
  letter-spacing: 1px;
@@ -618,6 +636,15 @@ textarea:focus {
618
636
  transform: var(--p-lift-sm);
619
637
  }
620
638
 
639
+ /* Checkboxes, radio buttons and disclosure summaries: the focus ring of links
640
+ and buttons, without the lift. */
641
+ input[type="checkbox"]:focus-visible,
642
+ input[type="radio"]:focus-visible,
643
+ summary:focus-visible {
644
+ outline: var(--p-border-width) solid var(--p-accent-2);
645
+ outline-offset: 2px;
646
+ }
647
+
621
648
 
622
649
  /* ══════════════════════════════════════════
623
650
  STATUS MESSAGES
@@ -842,11 +869,38 @@ textarea:focus {
842
869
  /* The current item: a pink bar, beside any focus outline, never instead of it. */
843
870
  .navbar-menu-panel a[aria-current],
844
871
  .navbar-menu-panel a[aria-current]:focus-visible { box-shadow: inset 3px 0 0 var(--p-accent-1); }
845
- .navbar-menu-panel form {
872
+ .navbar-menu-panel form,
873
+ .navbar-menu-group {
846
874
  margin: var(--p-space-xs) 0 0;
847
875
  padding-top: var(--p-space-xs);
848
876
  border-top: var(--p-border-thin) solid var(--p-border);
849
877
  }
878
+ /* A group of items under a thin rule, with an optional .navbar-menu-label. */
879
+ .navbar-menu-group { display: grid; }
880
+ .navbar-menu-group:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
881
+ /* A setting: its name on the left, a switch on the right, on and off from
882
+ aria-pressed. The switch is square-edged like everything else. */
883
+ .navbar-menu-panel .navbar-menu-setting {
884
+ display: flex;
885
+ align-items: center;
886
+ justify-content: space-between;
887
+ gap: var(--p-space-md);
888
+ }
889
+ .navbar-menu-setting::after {
890
+ content: '';
891
+ flex: none;
892
+ width: 28px;
893
+ height: 16px;
894
+ border: var(--p-border-width) solid var(--p-text);
895
+ background: var(--p-surface) linear-gradient(var(--p-text), var(--p-text)) no-repeat left 2px center / 8px 8px;
896
+ }
897
+ .navbar-menu-setting[aria-pressed="true"]::after {
898
+ background: var(--p-accent-1) linear-gradient(var(--p-text), var(--p-text)) no-repeat right 2px center / 8px 8px;
899
+ }
900
+ @media (forced-colors: active) {
901
+ .navbar-menu-setting::after { forced-color-adjust: none; border-color: CanvasText; background-color: Canvas; background-image: linear-gradient(CanvasText, CanvasText); }
902
+ .navbar-menu-setting[aria-pressed="true"]::after { background-color: Highlight; }
903
+ }
850
904
 
851
905
  /* ══════════════════════════════════════════
852
906
  TABLES