palace-ui 0.10.0 → 0.12.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/SPEC.md +33 -4
- package/catalog.html +23 -1
- package/package.json +5 -1
- package/palace.css +153 -7
package/SPEC.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
- **Retro-technical.** Hard edges (border-radius: 0 everywhere), thick 2px borders, chunky offset box-shadows. Gradients on text, thin decorative stripes (≤3px), and narrow fixed bars (e.g. sticky CTAs) only — never on large surfaces or card backgrounds.
|
|
10
10
|
- **Three-color accent system.** Hot pink `#ff2d9b`, electric purple `#5b4cff`, cyan `#00e5ff`. These rotate across card lists and provide visual rhythm.
|
|
11
11
|
- **Snappy interactions.** All transitions are `0.1s`. Hovers lift elements up-left and cast a hard shadow down-right. Fast, mechanical feedback.
|
|
12
|
-
- **Uppercase everything.** All headings, labels
|
|
12
|
+
- **Uppercase everything.** All headings, labels and buttons are `text-transform: uppercase` with tracked letter-spacing. Body text and status messages, which are sentences, are the exception.
|
|
13
13
|
- **Dark on light.** `#0a0a0a` text on `#f0f0f0` background. Cards are white `#fff`. Code blocks invert to dark `#0a0a0a` backgrounds.
|
|
14
14
|
- **No border radius.** Ever. `border-radius: 0` is the rule, not the exception.
|
|
15
15
|
- **Content-width containers.** Max-width `640px` for main content, `420px` for auth/narrow forms.
|
|
@@ -53,7 +53,8 @@ Or if consuming via npm with a build tool:
|
|
|
53
53
|
| `--p-surface` | `#fff` | Card/container backgrounds |
|
|
54
54
|
| `--p-surface-inset` | `#f7f7f7` | Inset/recessed surfaces |
|
|
55
55
|
| `--p-dark` | `#0a0a0a` | Dark backgrounds (navbar, code, hover fills) |
|
|
56
|
-
| `--p-error` | `#ff4444` | Error states, danger zones |
|
|
56
|
+
| `--p-error` | `#ff4444` | Error states, danger zones: fills, borders, the error box |
|
|
57
|
+
| `--p-error-text` | `#c40000` | Red text (danger buttons, error text): 6.3:1 on white, where `#ff4444` is 3.4:1 |
|
|
57
58
|
| `--p-success` | `#00ff88` | Success states |
|
|
58
59
|
| `--p-warning` | `#ffcc00` | Warning states |
|
|
59
60
|
| `--p-info` | `var(--p-accent-3)` | Informational states (semantic alias for cyan) |
|
|
@@ -137,6 +138,27 @@ Every page follows this structure:
|
|
|
137
138
|
</nav>
|
|
138
139
|
```
|
|
139
140
|
|
|
141
|
+
A navbar menu for the signed-in user is a `<details class="navbar-menu">`: the summary looks like `.navbar-user`, and the panel's links and buttons are its items. A form in the panel (sign out) sits under a thin rule. It works without JavaScript; a script can add Escape and closing from outside.
|
|
142
|
+
|
|
143
|
+
```html
|
|
144
|
+
<nav class="navbar">
|
|
145
|
+
<a href="/" class="navbar-brand">App Name</a>
|
|
146
|
+
<div class="navbar-right">
|
|
147
|
+
<details class="navbar-menu">
|
|
148
|
+
<summary><span class="visually-hidden">Account: </span>Username</summary>
|
|
149
|
+
<div class="navbar-menu-panel">
|
|
150
|
+
<p class="navbar-menu-label" aria-hidden="true">Your teams</p>
|
|
151
|
+
<ul aria-label="Your teams"><li><a href="/a" aria-current="true">Team A</a></li></ul>
|
|
152
|
+
<a href="/account">Account</a>
|
|
153
|
+
<form method="post" action="/logout"><button type="submit">Sign out</button></form>
|
|
154
|
+
</div>
|
|
155
|
+
</details>
|
|
156
|
+
</div>
|
|
157
|
+
</nav>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`.visually-hidden` hides text visually and keeps it for screen readers.
|
|
161
|
+
|
|
140
162
|
With an icon beside the brand name (`.navbar-brand` uses `inline-flex` layout with `gap: 10px`; images/SVGs are auto-sized to 24×24):
|
|
141
163
|
|
|
142
164
|
```html
|
|
@@ -321,6 +343,13 @@ Size modifiers compose with any color variant:
|
|
|
321
343
|
</div>
|
|
322
344
|
```
|
|
323
345
|
|
|
346
|
+
A `<select>` is styled like the text inputs, with a drawn chevron:
|
|
347
|
+
|
|
348
|
+
```html
|
|
349
|
+
<label for="direction">Direction</label>
|
|
350
|
+
<select id="direction"><option>Anticlockwise</option><option>Clockwise</option></select>
|
|
351
|
+
```
|
|
352
|
+
|
|
324
353
|
Inline form (input + button side by side):
|
|
325
354
|
|
|
326
355
|
```html
|
|
@@ -697,12 +726,12 @@ Full container, no navbar. Hero section, then card list, then footer.
|
|
|
697
726
|
- **accent-3 (cyan):** Navbar user name, code highlights, key reveal borders, third card
|
|
698
727
|
- **Never mix** accent colors in a single element (except the gradient on h1 text)
|
|
699
728
|
- **The 3-color rotation** applies to any list of sibling cards: 1=pink, 2=purple, 3=cyan, 4=pink...
|
|
700
|
-
- **Error is always `#ff4444`**, success is always `#00ff88`, warning is `#ffcc00`, info is `#00e5ff`. Status colors are not accent colors.
|
|
729
|
+
- **Error is always `#ff4444`**, success is always `#00ff88`, warning is `#ffcc00`, info is `#00e5ff`. Status colors are not accent colors. Text on a status color is always dark `--p-text`; red text uses `--p-error-text`, never `#ff4444`.
|
|
701
730
|
|
|
702
731
|
## Do's and Don'ts
|
|
703
732
|
|
|
704
733
|
### Do
|
|
705
|
-
- Use uppercase for headings, labels, buttons
|
|
734
|
+
- Use uppercase for headings, labels, buttons and badges; status messages are sentences in sentence case
|
|
706
735
|
- Use the 3-color card rotation for any list of cards
|
|
707
736
|
- Keep `border-radius: 0` everywhere
|
|
708
737
|
- Use `2px solid` borders on cards, buttons, inputs
|
package/catalog.html
CHANGED
|
@@ -424,7 +424,7 @@
|
|
|
424
424
|
<div class="container">
|
|
425
425
|
<p class="eyebrow" style="color: var(--p-accent-1); margin-bottom: 8px;">Components</p>
|
|
426
426
|
<h2 id="forms">Forms</h2>
|
|
427
|
-
<p class="subtitle" style="margin-bottom: 24px;">Labels, text inputs, number input, textarea.</p>
|
|
427
|
+
<p class="subtitle" style="margin-bottom: 24px;">Labels, text inputs, select, number input, textarea.</p>
|
|
428
428
|
|
|
429
429
|
<div data-source class="card-static" style="margin-bottom: 16px;">
|
|
430
430
|
<div class="stack-md">
|
|
@@ -444,6 +444,10 @@
|
|
|
444
444
|
<label for="cat-search">Search</label>
|
|
445
445
|
<input type="search" id="cat-search" placeholder="Search...">
|
|
446
446
|
</div>
|
|
447
|
+
<div>
|
|
448
|
+
<label for="cat-direction">Direction</label>
|
|
449
|
+
<select id="cat-direction"><option>Anticlockwise</option><option>Clockwise</option></select>
|
|
450
|
+
</div>
|
|
447
451
|
<div>
|
|
448
452
|
<label for="cat-num">Quantity</label>
|
|
449
453
|
<input type="number" id="cat-num" value="5" min="0" max="99">
|
|
@@ -789,6 +793,24 @@
|
|
|
789
793
|
</nav>
|
|
790
794
|
</div>
|
|
791
795
|
|
|
796
|
+
<h3 style="margin-bottom: 12px;">With a navbar menu (open it)</h3>
|
|
797
|
+
<div data-source style="margin-bottom: 24px; padding-bottom: 180px;">
|
|
798
|
+
<nav class="navbar">
|
|
799
|
+
<a href="#" class="navbar-brand">App Name</a>
|
|
800
|
+
<div class="navbar-right">
|
|
801
|
+
<details class="navbar-menu">
|
|
802
|
+
<summary><span class="visually-hidden">Account: </span>Username</summary>
|
|
803
|
+
<div class="navbar-menu-panel">
|
|
804
|
+
<p class="navbar-menu-label" aria-hidden="true">Your teams</p>
|
|
805
|
+
<ul aria-label="Your teams"><li><a href="#" aria-current="true">Team A</a></li><li><a href="#">Team B</a></li></ul>
|
|
806
|
+
<a href="#">Account</a>
|
|
807
|
+
<form><button type="button">Sign out</button></form>
|
|
808
|
+
</div>
|
|
809
|
+
</details>
|
|
810
|
+
</div>
|
|
811
|
+
</nav>
|
|
812
|
+
</div>
|
|
813
|
+
|
|
792
814
|
<h3 style="margin-bottom: 12px;">Light (.navbar-light)</h3>
|
|
793
815
|
<div data-source>
|
|
794
816
|
<nav class="navbar navbar-light" style="border: 2px solid var(--p-border);">
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "palace-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Retro-technical design system. Hard edges, vibrant gradients, chunky shadows.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/jostsg/palace-ui.git"
|
|
9
|
+
},
|
|
6
10
|
"type": "module",
|
|
7
11
|
"main": "palace.css",
|
|
8
12
|
"style": "palace.css",
|
package/palace.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! palace-ui v0.
|
|
1
|
+
/*! palace-ui v0.12.0 | MIT License | fonts: SIL OFL 1.1, see fonts/ */
|
|
2
2
|
|
|
3
3
|
/* ══════════════════════════════════════════
|
|
4
4
|
FONTS (self-hosted; no third-party requests)
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
/* ── Status ── */
|
|
43
43
|
--p-error: #ff4444;
|
|
44
|
+
--p-error-text: #c40000; /* red for text: 6.3:1 on white, where #ff4444 is 3.4:1 */
|
|
44
45
|
--p-success: #00ff88;
|
|
45
46
|
--p-warning: #ffcc00;
|
|
46
47
|
--p-info: var(--p-accent-3);
|
|
@@ -110,6 +111,19 @@
|
|
|
110
111
|
padding: 0;
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
/* The hidden attribute always hides, even on elements palace gives a display (buttons, .btn). */
|
|
115
|
+
[hidden] { display: none !important; }
|
|
116
|
+
|
|
117
|
+
/* Text for screen readers only: off screen, still read. */
|
|
118
|
+
.visually-hidden {
|
|
119
|
+
position: absolute;
|
|
120
|
+
width: 1px;
|
|
121
|
+
height: 1px;
|
|
122
|
+
overflow: hidden;
|
|
123
|
+
clip-path: inset(50%);
|
|
124
|
+
white-space: nowrap;
|
|
125
|
+
}
|
|
126
|
+
|
|
113
127
|
body {
|
|
114
128
|
font-family: var(--p-font);
|
|
115
129
|
color: var(--p-text);
|
|
@@ -367,12 +381,12 @@ button, .btn {
|
|
|
367
381
|
|
|
368
382
|
.btn-danger {
|
|
369
383
|
background: transparent;
|
|
370
|
-
color: var(--p-error);
|
|
384
|
+
color: var(--p-error-text);
|
|
371
385
|
border-color: var(--p-error);
|
|
372
386
|
}
|
|
373
387
|
.btn-danger:hover {
|
|
374
388
|
background: var(--p-error);
|
|
375
|
-
color:
|
|
389
|
+
color: var(--p-text);
|
|
376
390
|
transform: var(--p-lift-sm);
|
|
377
391
|
box-shadow: var(--p-shadow-sm) var(--p-dark);
|
|
378
392
|
}
|
|
@@ -534,6 +548,32 @@ input[type="tel"]:focus {
|
|
|
534
548
|
transform: var(--p-lift-sm);
|
|
535
549
|
}
|
|
536
550
|
|
|
551
|
+
select {
|
|
552
|
+
width: 100%;
|
|
553
|
+
appearance: none;
|
|
554
|
+
padding: 12px 2.5rem 12px 12px;
|
|
555
|
+
border: var(--p-border-width) solid var(--p-text);
|
|
556
|
+
border-radius: var(--p-radius);
|
|
557
|
+
background-color: var(--p-surface);
|
|
558
|
+
/* A chevron drawn with two gradients: no image, no font. */
|
|
559
|
+
background-image: linear-gradient(45deg, transparent 50%, var(--p-text) 50%), linear-gradient(135deg, var(--p-text) 50%, transparent 50%);
|
|
560
|
+
background-position: calc(100% - 1.25rem) 50%, calc(100% - 0.9rem) 50%;
|
|
561
|
+
background-size: 0.35rem 0.35rem;
|
|
562
|
+
background-repeat: no-repeat;
|
|
563
|
+
font-family: var(--p-font);
|
|
564
|
+
font-size: 1rem;
|
|
565
|
+
color: var(--p-text);
|
|
566
|
+
outline: none;
|
|
567
|
+
transition: var(--p-transition);
|
|
568
|
+
}
|
|
569
|
+
select[multiple], select[size]:not([size="1"]) { padding-right: 12px; background-image: none; }
|
|
570
|
+
|
|
571
|
+
select:focus {
|
|
572
|
+
border-color: var(--p-accent-2);
|
|
573
|
+
box-shadow: var(--p-shadow-md) var(--p-accent-2);
|
|
574
|
+
transform: var(--p-lift-sm);
|
|
575
|
+
}
|
|
576
|
+
|
|
537
577
|
input::placeholder {
|
|
538
578
|
color: var(--p-border-dashed);
|
|
539
579
|
}
|
|
@@ -583,12 +623,11 @@ textarea:focus {
|
|
|
583
623
|
STATUS MESSAGES
|
|
584
624
|
══════════════════════════════════════════ */
|
|
585
625
|
|
|
626
|
+
/* A status message is a sentence, so it reads in sentence case, like body text. */
|
|
586
627
|
.status {
|
|
587
628
|
padding: 12px;
|
|
588
629
|
font-size: 13px;
|
|
589
630
|
font-weight: 600;
|
|
590
|
-
text-transform: uppercase;
|
|
591
|
-
letter-spacing: 0.5px;
|
|
592
631
|
border: var(--p-border-width) solid var(--p-text);
|
|
593
632
|
}
|
|
594
633
|
|
|
@@ -600,7 +639,7 @@ textarea:focus {
|
|
|
600
639
|
|
|
601
640
|
.status-error {
|
|
602
641
|
background: var(--p-error);
|
|
603
|
-
color: #
|
|
642
|
+
color: var(--p-text); /* 5.8:1 on #ff4444; white would be 3.4:1 */
|
|
604
643
|
border-color: var(--p-text);
|
|
605
644
|
}
|
|
606
645
|
|
|
@@ -691,6 +730,11 @@ textarea:focus {
|
|
|
691
730
|
color: var(--p-accent-2);
|
|
692
731
|
}
|
|
693
732
|
|
|
733
|
+
.navbar-light .navbar-menu > summary {
|
|
734
|
+
color: var(--p-accent-2);
|
|
735
|
+
border-color: var(--p-border);
|
|
736
|
+
}
|
|
737
|
+
|
|
694
738
|
.navbar-light .btn {
|
|
695
739
|
color: var(--p-text-secondary);
|
|
696
740
|
border-color: var(--p-border);
|
|
@@ -702,6 +746,108 @@ textarea:focus {
|
|
|
702
746
|
}
|
|
703
747
|
|
|
704
748
|
|
|
749
|
+
/* Navbar menu: a <details> whose summary looks like .navbar-user and opens a
|
|
750
|
+
panel in the dialog card language. Works without JavaScript; a script may
|
|
751
|
+
add Escape and closing from outside. Links and buttons in the panel are its
|
|
752
|
+
items; a form in it (e.g. Sign out) sits under a thin rule. */
|
|
753
|
+
.navbar-menu { position: relative; }
|
|
754
|
+
.navbar-menu > summary {
|
|
755
|
+
position: relative;
|
|
756
|
+
display: block;
|
|
757
|
+
box-sizing: border-box;
|
|
758
|
+
max-width: min(14rem, 40vw);
|
|
759
|
+
/* The label truncates; the caret after it, in the right padding, never does. */
|
|
760
|
+
padding: 6px 28px 6px 14px;
|
|
761
|
+
overflow: hidden;
|
|
762
|
+
text-overflow: ellipsis;
|
|
763
|
+
white-space: nowrap;
|
|
764
|
+
border: var(--p-border-thin) solid var(--p-border-dark);
|
|
765
|
+
font: 600 11px/1.4 var(--p-font);
|
|
766
|
+
letter-spacing: 1px;
|
|
767
|
+
text-transform: uppercase;
|
|
768
|
+
color: var(--p-accent-3);
|
|
769
|
+
list-style: none;
|
|
770
|
+
cursor: pointer;
|
|
771
|
+
}
|
|
772
|
+
.navbar-menu > summary::-webkit-details-marker { display: none; }
|
|
773
|
+
.navbar-menu > summary::after {
|
|
774
|
+
content: '';
|
|
775
|
+
position: absolute;
|
|
776
|
+
right: 12px;
|
|
777
|
+
top: 50%;
|
|
778
|
+
margin-top: -2px;
|
|
779
|
+
border: 4px solid transparent;
|
|
780
|
+
border-top-color: currentColor;
|
|
781
|
+
border-bottom: 0;
|
|
782
|
+
}
|
|
783
|
+
.navbar-menu > summary:hover,
|
|
784
|
+
.navbar-menu[open] > summary { color: var(--p-accent-1); border-color: var(--p-accent-1); }
|
|
785
|
+
.navbar-menu > summary:focus-visible { outline: 2px solid var(--p-accent-3); outline-offset: 2px; }
|
|
786
|
+
|
|
787
|
+
/* Right-aligned under the summary: a navbar menu sits at the navbar's right. */
|
|
788
|
+
.navbar-menu-panel {
|
|
789
|
+
position: absolute;
|
|
790
|
+
right: 0;
|
|
791
|
+
top: calc(100% + 6px);
|
|
792
|
+
z-index: 100;
|
|
793
|
+
min-width: 14rem;
|
|
794
|
+
max-width: calc(100vw - 2 * var(--p-space-md));
|
|
795
|
+
display: grid;
|
|
796
|
+
padding: var(--p-space-xs) 0;
|
|
797
|
+
background: var(--p-surface);
|
|
798
|
+
color: var(--p-text);
|
|
799
|
+
border: var(--p-border-width) solid var(--p-text);
|
|
800
|
+
box-shadow: var(--p-shadow-md) var(--p-accent-2);
|
|
801
|
+
}
|
|
802
|
+
.navbar-menu-panel ul { list-style: none; margin: 0; padding: 0; display: grid; }
|
|
803
|
+
.navbar-menu-label {
|
|
804
|
+
margin: 0;
|
|
805
|
+
padding: var(--p-space-sm) var(--p-space-md) var(--p-space-xs);
|
|
806
|
+
font-size: 11px;
|
|
807
|
+
font-weight: 600;
|
|
808
|
+
letter-spacing: 1px;
|
|
809
|
+
text-transform: uppercase;
|
|
810
|
+
color: var(--p-text-faint);
|
|
811
|
+
}
|
|
812
|
+
.navbar-menu-panel a,
|
|
813
|
+
.navbar-menu-panel button {
|
|
814
|
+
display: block;
|
|
815
|
+
width: 100%;
|
|
816
|
+
box-sizing: border-box;
|
|
817
|
+
padding: var(--p-space-sm) var(--p-space-md);
|
|
818
|
+
border: 0;
|
|
819
|
+
background: none;
|
|
820
|
+
font: 600 13px/1.4 var(--p-font);
|
|
821
|
+
text-transform: none;
|
|
822
|
+
letter-spacing: normal;
|
|
823
|
+
color: var(--p-text);
|
|
824
|
+
text-align: left;
|
|
825
|
+
text-decoration: none;
|
|
826
|
+
cursor: pointer;
|
|
827
|
+
/* No transition: a closed <details> does not render its content, so a
|
|
828
|
+
transition started there would never finish. */
|
|
829
|
+
transition: none;
|
|
830
|
+
}
|
|
831
|
+
.navbar-menu-panel a:hover,
|
|
832
|
+
.navbar-menu-panel button:hover { background: var(--p-hover-bg); transform: none; box-shadow: none; }
|
|
833
|
+
/* One focus style for links and buttons: the button lift would spill past the panel. */
|
|
834
|
+
.navbar-menu-panel a:focus-visible,
|
|
835
|
+
.navbar-menu-panel button:focus-visible {
|
|
836
|
+
outline: 2px solid var(--p-accent-2);
|
|
837
|
+
outline-offset: -2px;
|
|
838
|
+
color: var(--p-accent-2);
|
|
839
|
+
transform: none;
|
|
840
|
+
box-shadow: none;
|
|
841
|
+
}
|
|
842
|
+
/* The current item: a pink bar, beside any focus outline, never instead of it. */
|
|
843
|
+
.navbar-menu-panel a[aria-current],
|
|
844
|
+
.navbar-menu-panel a[aria-current]:focus-visible { box-shadow: inset 3px 0 0 var(--p-accent-1); }
|
|
845
|
+
.navbar-menu-panel form {
|
|
846
|
+
margin: var(--p-space-xs) 0 0;
|
|
847
|
+
padding-top: var(--p-space-xs);
|
|
848
|
+
border-top: var(--p-border-thin) solid var(--p-border);
|
|
849
|
+
}
|
|
850
|
+
|
|
705
851
|
/* ══════════════════════════════════════════
|
|
706
852
|
TABLES
|
|
707
853
|
══════════════════════════════════════════ */
|
|
@@ -878,7 +1024,7 @@ pre {
|
|
|
878
1024
|
DANGER ZONE
|
|
879
1025
|
══════════════════════════════════════════ */
|
|
880
1026
|
|
|
881
|
-
.danger-zone h2 { color: var(--p-error); }
|
|
1027
|
+
.danger-zone h2 { color: var(--p-error-text); }
|
|
882
1028
|
.danger-zone p {
|
|
883
1029
|
color: var(--p-text-muted);
|
|
884
1030
|
font-size: 13px;
|