@junoput01/junoui 0.2.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 +199 -0
- package/README.md +117 -0
- package/dist/android/colors.xml +94 -0
- package/dist/android/dimens.xml +49 -0
- package/dist/css/juno-fonts.css +45 -0
- package/dist/css/juno-tokens.css +345 -0
- package/dist/css/juno.css +4481 -0
- package/dist/flutter/juno_tokens.dart +143 -0
- package/dist/fonts/b612-400.woff2 +0 -0
- package/dist/fonts/b612-700.woff2 +0 -0
- package/dist/fonts/b612-mono-400.woff2 +0 -0
- package/dist/fonts/b612-mono-700.woff2 +0 -0
- package/dist/icons/juno-icons.svg +69 -0
- package/dist/ios/JunoTokens.swift +149 -0
- package/dist/js/tokens.d.ts +11 -0
- package/dist/js/tokens.js +244 -0
- package/dist/json/tokens.json +921 -0
- package/dist/scss/_juno-tokens.scss +176 -0
- package/package.json +92 -0
- package/src/css/base.css +120 -0
- package/src/css/components/accordion.css +83 -0
- package/src/css/components/alert.css +90 -0
- package/src/css/components/avatar.css +82 -0
- package/src/css/components/badge.css +55 -0
- package/src/css/components/breadcrumb.css +64 -0
- package/src/css/components/button.css +59 -0
- package/src/css/components/card.css +68 -0
- package/src/css/components/checkbox.css +89 -0
- package/src/css/components/chip.css +89 -0
- package/src/css/components/divider.css +50 -0
- package/src/css/components/dock.css +74 -0
- package/src/css/components/dot.css +35 -0
- package/src/css/components/drawer.css +80 -0
- package/src/css/components/field.css +37 -0
- package/src/css/components/gauge.css +78 -0
- package/src/css/components/icon.css +35 -0
- package/src/css/components/input.css +60 -0
- package/src/css/components/list.css +135 -0
- package/src/css/components/loader.css +159 -0
- package/src/css/components/menu.css +114 -0
- package/src/css/components/modal.css +161 -0
- package/src/css/components/navbar.css +103 -0
- package/src/css/components/pagination.css +72 -0
- package/src/css/components/pillbar.css +111 -0
- package/src/css/components/popover.css +73 -0
- package/src/css/components/rail.css +96 -0
- package/src/css/components/readout.css +45 -0
- package/src/css/components/segmented.css +99 -0
- package/src/css/components/select.css +38 -0
- package/src/css/components/skeleton.css +62 -0
- package/src/css/components/slider.css +117 -0
- package/src/css/components/spark.css +26 -0
- package/src/css/components/stepper.css +121 -0
- package/src/css/components/switch.css +131 -0
- package/src/css/components/table.css +443 -0
- package/src/css/components/tabs.css +66 -0
- package/src/css/components/thumb.css +69 -0
- package/src/css/components/toast.css +109 -0
- package/src/css/components/toggle-button.css +91 -0
- package/src/css/components/tooltip.css +155 -0
- package/src/css/density.css +40 -0
- package/src/css/fonts.css +45 -0
- package/src/css/layout.css +99 -0
- package/src/css/typescale.css +27 -0
- package/src/css/utilities.css +76 -0
- package/src/fonts/LICENSE +93 -0
- package/src/fonts/b612-400.woff2 +0 -0
- package/src/fonts/b612-700.woff2 +0 -0
- package/src/fonts/b612-mono-400.woff2 +0 -0
- package/src/fonts/b612-mono-700.woff2 +0 -0
- package/src/icons/LICENSE +21 -0
- package/src/icons/arrow-clockwise.svg +1 -0
- package/src/icons/arrow-down.svg +1 -0
- package/src/icons/arrow-left.svg +1 -0
- package/src/icons/arrow-right.svg +1 -0
- package/src/icons/arrow-up.svg +1 -0
- package/src/icons/arrows-clockwise.svg +1 -0
- package/src/icons/arrows-out.svg +1 -0
- package/src/icons/bell.svg +1 -0
- package/src/icons/calendar-blank.svg +1 -0
- package/src/icons/caret-down.svg +1 -0
- package/src/icons/caret-left.svg +1 -0
- package/src/icons/caret-right.svg +1 -0
- package/src/icons/caret-up.svg +1 -0
- package/src/icons/chat-circle.svg +1 -0
- package/src/icons/check-circle.svg +1 -0
- package/src/icons/check.svg +1 -0
- package/src/icons/clock.svg +1 -0
- package/src/icons/cloud-arrow-down.svg +1 -0
- package/src/icons/cloud.svg +1 -0
- package/src/icons/copy.svg +1 -0
- package/src/icons/cpu.svg +1 -0
- package/src/icons/dots-three-vertical.svg +1 -0
- package/src/icons/dots-three.svg +1 -0
- package/src/icons/download.svg +1 -0
- package/src/icons/envelope.svg +1 -0
- package/src/icons/eye-slash.svg +1 -0
- package/src/icons/eye.svg +1 -0
- package/src/icons/file.svg +1 -0
- package/src/icons/film-strip.svg +1 -0
- package/src/icons/floppy-disk.svg +1 -0
- package/src/icons/folder.svg +1 -0
- package/src/icons/funnel.svg +1 -0
- package/src/icons/gear.svg +1 -0
- package/src/icons/hard-drives.svg +1 -0
- package/src/icons/heart.svg +1 -0
- package/src/icons/hexagon.svg +1 -0
- package/src/icons/house.svg +1 -0
- package/src/icons/images.svg +1 -0
- package/src/icons/info.svg +1 -0
- package/src/icons/link.svg +1 -0
- package/src/icons/list.svg +1 -0
- package/src/icons/lock-open.svg +1 -0
- package/src/icons/lock.svg +1 -0
- package/src/icons/magnifying-glass.svg +1 -0
- package/src/icons/minus.svg +1 -0
- package/src/icons/moon.svg +1 -0
- package/src/icons/pencil-simple.svg +1 -0
- package/src/icons/play.svg +1 -0
- package/src/icons/plus.svg +1 -0
- package/src/icons/puzzle-piece.svg +1 -0
- package/src/icons/question.svg +1 -0
- package/src/icons/sliders-horizontal.svg +1 -0
- package/src/icons/sliders.svg +1 -0
- package/src/icons/squares-four.svg +1 -0
- package/src/icons/star.svg +1 -0
- package/src/icons/sun.svg +1 -0
- package/src/icons/trash.svg +1 -0
- package/src/icons/upload-simple.svg +1 -0
- package/src/icons/upload.svg +1 -0
- package/src/icons/user.svg +1 -0
- package/src/icons/users.svg +1 -0
- package/src/icons/warning-circle.svg +1 -0
- package/src/icons/warning.svg +1 -0
- package/src/icons/x-circle.svg +1 -0
- package/src/icons/x.svg +1 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/* ════════════════════════════════════════════════════════════════════
|
|
2
|
+
* Component — Modal / Dialog
|
|
3
|
+
* Style a native <dialog>. The app owns open/close (dialog.showModal() /
|
|
4
|
+
* .close()) and focus-trapping; junoui ships the look + entry motion.
|
|
5
|
+
* Centered, scrim-backed. Reserved for decisions that must block the deck.
|
|
6
|
+
* Usage:
|
|
7
|
+
* <dialog class="juno-modal"> (or .juno-modal.juno--warning)
|
|
8
|
+
* <div class="juno-modal__stripe"></div>
|
|
9
|
+
* <div class="juno-modal__head">
|
|
10
|
+
* <span class="juno-modal__tag">CONFIGURATION</span>
|
|
11
|
+
* <button class="juno-modal__close" aria-label="Close">✕</button>
|
|
12
|
+
* </div>
|
|
13
|
+
* <div class="juno-modal__body">
|
|
14
|
+
* <h2 class="juno-modal__title">Apply configuration change?</h2>
|
|
15
|
+
* <p class="juno-modal__text">…</p>
|
|
16
|
+
* <div class="juno-modal__foot">
|
|
17
|
+
* <button class="juno-btn juno-btn--ghost">CANCEL</button>
|
|
18
|
+
* <button class="juno-btn">APPLY</button>
|
|
19
|
+
* </div>
|
|
20
|
+
* </div>
|
|
21
|
+
* </dialog>
|
|
22
|
+
* ════════════════════════════════════════════════════════════════════ */
|
|
23
|
+
|
|
24
|
+
.juno-modal {
|
|
25
|
+
--juno-role: var(--juno-active);
|
|
26
|
+
|
|
27
|
+
inline-size: 440px;
|
|
28
|
+
max-inline-size: 100%;
|
|
29
|
+
padding: 0;
|
|
30
|
+
background: var(--juno-s1);
|
|
31
|
+
color: var(--juno-data);
|
|
32
|
+
border: var(--juno-border-width-1) solid var(--juno-border);
|
|
33
|
+
border-radius: var(--juno-radius-8);
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
box-shadow: var(--juno-shadow-3);
|
|
36
|
+
|
|
37
|
+
/* enter: scale + rise, decelerating (deliberate — it blocks the deck) */
|
|
38
|
+
opacity: 0;
|
|
39
|
+
transform: translateY(var(--juno-space-12)) scale(0.97);
|
|
40
|
+
transition:
|
|
41
|
+
opacity var(--juno-motion-duration-deliberate) var(--juno-motion-ease-decel),
|
|
42
|
+
transform var(--juno-motion-duration-deliberate) var(--juno-motion-ease-decel),
|
|
43
|
+
overlay var(--juno-motion-duration-deliberate) allow-discrete,
|
|
44
|
+
display var(--juno-motion-duration-deliberate) allow-discrete;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.juno-modal[open] {
|
|
48
|
+
opacity: 1;
|
|
49
|
+
transform: translateY(0) scale(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@starting-style {
|
|
53
|
+
.juno-modal[open] {
|
|
54
|
+
opacity: 0;
|
|
55
|
+
transform: translateY(var(--juno-space-12)) scale(0.97);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* scrim — one at a time; dark wash + faint blur */
|
|
60
|
+
.juno-modal::backdrop {
|
|
61
|
+
background: rgb(0 0 0);
|
|
62
|
+
opacity: var(--juno-opacity-scrim);
|
|
63
|
+
backdrop-filter: blur(1.5px);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* status accent stripe across the top (role-colored) */
|
|
67
|
+
.juno-modal__stripe {
|
|
68
|
+
block-size: var(--juno-border-width-3);
|
|
69
|
+
background: var(--juno-role);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.juno-modal__head {
|
|
73
|
+
display: flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
gap: var(--juno-space-8);
|
|
76
|
+
padding: var(--juno-space-16) var(--juno-pad-surface-inline) 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.juno-modal__tag {
|
|
80
|
+
font-family: var(--juno-font-family-mono);
|
|
81
|
+
font-size: var(--juno-font-size-10);
|
|
82
|
+
letter-spacing: var(--juno-font-tracking-wider);
|
|
83
|
+
text-transform: uppercase;
|
|
84
|
+
color: var(--juno-role);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.juno-modal__close {
|
|
88
|
+
margin-inline-start: auto;
|
|
89
|
+
inline-size: var(--juno-size-tap-min);
|
|
90
|
+
block-size: var(--juno-size-tap-min);
|
|
91
|
+
display: inline-flex;
|
|
92
|
+
align-items: center;
|
|
93
|
+
justify-content: center;
|
|
94
|
+
background: transparent;
|
|
95
|
+
border: var(--juno-border-width-1) solid transparent;
|
|
96
|
+
border-radius: var(--juno-radius-4);
|
|
97
|
+
color: var(--juno-label);
|
|
98
|
+
cursor: pointer;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.juno-modal__close:hover { background: var(--juno-s2); color: var(--juno-data); }
|
|
102
|
+
|
|
103
|
+
.juno-modal__body { padding: var(--juno-space-12) var(--juno-pad-surface-inline) var(--juno-pad-surface-inline); }
|
|
104
|
+
|
|
105
|
+
.juno-modal__title {
|
|
106
|
+
font-family: var(--juno-font-family-sans);
|
|
107
|
+
font-size: var(--juno-font-size-20);
|
|
108
|
+
font-weight: var(--juno-font-weight-light);
|
|
109
|
+
line-height: var(--juno-font-lineHeight-snug);
|
|
110
|
+
color: var(--juno-data);
|
|
111
|
+
margin-block-end: var(--juno-space-12);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.juno-modal__text {
|
|
115
|
+
font-size: var(--juno-font-size-13);
|
|
116
|
+
line-height: var(--juno-font-lineHeight-loose);
|
|
117
|
+
color: var(--juno-label);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.juno-modal__foot {
|
|
121
|
+
display: flex;
|
|
122
|
+
gap: var(--juno-gap-control);
|
|
123
|
+
justify-content: flex-end;
|
|
124
|
+
margin-block-start: var(--juno-space-24);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* ── narrow viewports — the centered dialog becomes a bottom sheet ──────
|
|
128
|
+
Phones get the native-feeling pattern: full-width, pinned to the bottom
|
|
129
|
+
edge, top corners rounded, sliding up. Drawers opt out (:not) — they
|
|
130
|
+
already pin themselves. Scoped to the viewport (not a container): a
|
|
131
|
+
modal always lives in the top layer, so the screen IS its container. */
|
|
132
|
+
@media (width <= 639.98px) {
|
|
133
|
+
.juno-modal:not(.juno-drawer) {
|
|
134
|
+
inline-size: 100%;
|
|
135
|
+
max-inline-size: 100%;
|
|
136
|
+
max-block-size: 85dvh;
|
|
137
|
+
margin-block: auto 0;
|
|
138
|
+
margin-inline: 0;
|
|
139
|
+
border: none;
|
|
140
|
+
border-block-start: var(--juno-border-width-1) solid var(--juno-border);
|
|
141
|
+
border-start-start-radius: var(--juno-radius-8);
|
|
142
|
+
border-start-end-radius: var(--juno-radius-8);
|
|
143
|
+
border-end-start-radius: 0;
|
|
144
|
+
border-end-end-radius: 0;
|
|
145
|
+
transform: translateY(100%);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.juno-modal:not(.juno-drawer)[open] { transform: translateY(0); }
|
|
149
|
+
|
|
150
|
+
@starting-style {
|
|
151
|
+
.juno-modal:not(.juno-drawer)[open] { transform: translateY(100%); }
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/* keep the foot's actions above the home indicator */
|
|
155
|
+
.juno-modal:not(.juno-drawer) .juno-modal__body {
|
|
156
|
+
padding-block-end: calc(var(--juno-pad-surface-inline) + env(safe-area-inset-bottom, 0));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* full-width actions read better on a sheet */
|
|
160
|
+
.juno-modal:not(.juno-drawer) .juno-modal__foot > .juno-btn { flex: 1; }
|
|
161
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/* ════════════════════════════════════════════════════════════════════
|
|
2
|
+
* Component — Navbar (stack-navigation top bar)
|
|
3
|
+
* The header of a pushed view on phones: a back control on the start
|
|
4
|
+
* edge — always, so any screen can unwind — a centered truncating
|
|
5
|
+
* title, and trailing actions. junoui ships the look; the app owns the
|
|
6
|
+
* navigation stack (what "back" does).
|
|
7
|
+
*
|
|
8
|
+
* Sticky top, safe-area padded. Pair with the dock or pillbar for the
|
|
9
|
+
* tab + stack recipe: tabs switch sections, the navbar backs out of
|
|
10
|
+
* drilled-in views (see docs/layout.md#app-shell).
|
|
11
|
+
* Usage:
|
|
12
|
+
* <header class="juno-navbar">
|
|
13
|
+
* <a class="juno-navbar__back" href="…">
|
|
14
|
+
* <svg class="juno-icon" aria-hidden="true"><use href="…#juno-i-caret-left" /></svg>
|
|
15
|
+
* <span class="juno-navbar__back-label">Library</span>
|
|
16
|
+
* </a>
|
|
17
|
+
* <h1 class="juno-navbar__title">Album</h1>
|
|
18
|
+
* <div class="juno-navbar__actions">
|
|
19
|
+
* <button class="juno-btn juno-btn--sm juno-btn--ghost">EDIT</button>
|
|
20
|
+
* </div>
|
|
21
|
+
* </header>
|
|
22
|
+
* ════════════════════════════════════════════════════════════════════ */
|
|
23
|
+
|
|
24
|
+
.juno-navbar {
|
|
25
|
+
position: sticky;
|
|
26
|
+
inset-block-start: 0;
|
|
27
|
+
z-index: var(--juno-z-raised);
|
|
28
|
+
display: grid;
|
|
29
|
+
|
|
30
|
+
/* sides never shrink below their (already-truncated) content, so a long
|
|
31
|
+
title ellipsizes instead of sliding under the back control; with a short
|
|
32
|
+
title the equal 1fr sides keep it optically centered */
|
|
33
|
+
grid-template-columns: minmax(max-content, 1fr) minmax(0, auto) minmax(max-content, 1fr);
|
|
34
|
+
align-items: center;
|
|
35
|
+
gap: var(--juno-space-8);
|
|
36
|
+
padding-inline: var(--juno-space-8);
|
|
37
|
+
padding-block-start: env(safe-area-inset-top, 0);
|
|
38
|
+
min-block-size: var(--juno-size-tap-comfortable);
|
|
39
|
+
background: var(--juno-s1);
|
|
40
|
+
border-block-end: var(--juno-border-width-1) solid var(--juno-border);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.juno-navbar__back {
|
|
44
|
+
appearance: none;
|
|
45
|
+
margin: 0;
|
|
46
|
+
border: none;
|
|
47
|
+
background: transparent;
|
|
48
|
+
justify-self: start;
|
|
49
|
+
display: inline-flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
gap: var(--juno-space-4);
|
|
52
|
+
min-block-size: var(--juno-size-tap-comfortable);
|
|
53
|
+
min-inline-size: var(--juno-size-tap-comfortable);
|
|
54
|
+
padding-inline: var(--juno-space-4);
|
|
55
|
+
font-family: var(--juno-font-family-sans);
|
|
56
|
+
font-size: var(--juno-font-size-11);
|
|
57
|
+
font-weight: var(--juno-font-weight-semibold);
|
|
58
|
+
letter-spacing: var(--juno-font-tracking-label);
|
|
59
|
+
text-transform: uppercase;
|
|
60
|
+
text-decoration: none;
|
|
61
|
+
color: var(--juno-active);
|
|
62
|
+
cursor: pointer;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* RTL: the caret glyph must point "back" in either direction */
|
|
66
|
+
[dir='rtl'] .juno-navbar__back .juno-icon { transform: scaleX(-1); }
|
|
67
|
+
|
|
68
|
+
.juno-navbar__back:hover { color: var(--juno-data); }
|
|
69
|
+
|
|
70
|
+
.juno-navbar__back-label {
|
|
71
|
+
max-inline-size: 14ch;
|
|
72
|
+
overflow: hidden;
|
|
73
|
+
text-overflow: ellipsis;
|
|
74
|
+
white-space: nowrap;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.juno-navbar__title {
|
|
78
|
+
grid-column: 2;
|
|
79
|
+
margin: 0;
|
|
80
|
+
min-inline-size: 0;
|
|
81
|
+
max-inline-size: 100%;
|
|
82
|
+
font-family: var(--juno-font-family-sans);
|
|
83
|
+
font-size: var(--juno-font-size-14);
|
|
84
|
+
font-weight: var(--juno-font-weight-semibold);
|
|
85
|
+
color: var(--juno-data);
|
|
86
|
+
overflow: hidden;
|
|
87
|
+
text-overflow: ellipsis;
|
|
88
|
+
white-space: nowrap;
|
|
89
|
+
text-align: center;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.juno-navbar__actions {
|
|
93
|
+
grid-column: 3;
|
|
94
|
+
justify-self: end;
|
|
95
|
+
display: inline-flex;
|
|
96
|
+
align-items: center;
|
|
97
|
+
gap: var(--juno-space-4);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.juno-navbar__back:focus-visible {
|
|
101
|
+
outline: var(--juno-border-width-2) solid var(--juno-active);
|
|
102
|
+
outline-offset: calc(-1 * var(--juno-space-2));
|
|
103
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/* ════════════════════════════════════════════════════════════════════
|
|
2
|
+
* Component — Pagination · page navigation
|
|
3
|
+
* A row of page controls: prev / next plus numbered pages, with the
|
|
4
|
+
* current page marked aria-current="page". Built on a real <nav>; the
|
|
5
|
+
* app owns which page is active and renders the list. Zero JS.
|
|
6
|
+
* Usage:
|
|
7
|
+
* <nav class="juno-pagination" aria-label="Pagination">
|
|
8
|
+
* <button class="juno-pagination__item" aria-label="Previous" disabled>‹</button>
|
|
9
|
+
* <button class="juno-pagination__item" aria-current="page">1</button>
|
|
10
|
+
* <button class="juno-pagination__item">2</button>
|
|
11
|
+
* <span class="juno-pagination__gap">…</span>
|
|
12
|
+
* <button class="juno-pagination__item">9</button>
|
|
13
|
+
* <button class="juno-pagination__item" aria-label="Next">›</button>
|
|
14
|
+
* </nav>
|
|
15
|
+
* ════════════════════════════════════════════════════════════════════ */
|
|
16
|
+
|
|
17
|
+
.juno-pagination {
|
|
18
|
+
display: inline-flex;
|
|
19
|
+
flex-wrap: wrap;
|
|
20
|
+
align-items: center;
|
|
21
|
+
gap: var(--juno-space-4);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.juno-pagination__item {
|
|
25
|
+
display: inline-grid;
|
|
26
|
+
place-content: center;
|
|
27
|
+
min-inline-size: var(--juno-size-tap-min, var(--juno-space-32));
|
|
28
|
+
block-size: var(--juno-space-32);
|
|
29
|
+
padding-inline: var(--juno-space-8);
|
|
30
|
+
border: var(--juno-border-width-1) solid var(--juno-control-edge-strong);
|
|
31
|
+
border-radius: var(--juno-radius-4);
|
|
32
|
+
background: var(--juno-control-surface);
|
|
33
|
+
color: var(--juno-data);
|
|
34
|
+
font-family: var(--juno-font-family-mono);
|
|
35
|
+
font-size: var(--juno-font-size-13);
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
transition:
|
|
38
|
+
border-color var(--juno-motion-duration-quick) var(--juno-motion-ease-standard),
|
|
39
|
+
background-color var(--juno-motion-duration-quick) var(--juno-motion-ease-standard);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.juno-pagination__item:hover:not(:disabled, [aria-current='page']) {
|
|
43
|
+
border-color: var(--juno-active);
|
|
44
|
+
color: var(--juno-active);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.juno-pagination__item:focus-visible {
|
|
48
|
+
outline: var(--juno-border-width-2) solid var(--juno-active);
|
|
49
|
+
outline-offset: var(--juno-space-2);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* current page — filled active accent, not actionable */
|
|
53
|
+
.juno-pagination__item[aria-current='page'] {
|
|
54
|
+
border-color: var(--juno-active);
|
|
55
|
+
background: color-mix(in srgb, var(--juno-active) 18%, var(--juno-control-surface));
|
|
56
|
+
color: var(--juno-active);
|
|
57
|
+
cursor: default;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.juno-pagination__item:disabled {
|
|
61
|
+
opacity: var(--juno-opacity-disabled);
|
|
62
|
+
cursor: not-allowed;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* the … gap between page ranges */
|
|
66
|
+
.juno-pagination__gap {
|
|
67
|
+
display: inline-grid;
|
|
68
|
+
place-content: center;
|
|
69
|
+
min-inline-size: var(--juno-space-20);
|
|
70
|
+
color: var(--juno-muted);
|
|
71
|
+
font-family: var(--juno-font-family-mono);
|
|
72
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/* ════════════════════════════════════════════════════════════════════
|
|
2
|
+
* Component — Pillbar (floating pill toolbar / tab bar)
|
|
3
|
+
* A floating rounded bar hovering above the content's bottom edge —
|
|
4
|
+
* the iOS-style alternative to the full-width dock. Holds 2–5 icon
|
|
5
|
+
* actions or destinations; the active one styles off aria-current
|
|
6
|
+
* (nav links) or aria-pressed (toggle buttons), zero JS.
|
|
7
|
+
*
|
|
8
|
+
* Sticky (not fixed): put it last inside the scrolling column and it
|
|
9
|
+
* floats over the content while staying in flow. Clears the home
|
|
10
|
+
* indicator via safe-area-inset.
|
|
11
|
+
* Usage:
|
|
12
|
+
* <nav class="juno-pillbar" aria-label="Primary">
|
|
13
|
+
* <a class="juno-pillbar__item" href="…" aria-current="page" aria-label="Library">
|
|
14
|
+
* <svg class="juno-icon" aria-hidden="true"><use href="…#juno-i-squares-four" /></svg>
|
|
15
|
+
* <span class="juno-pillbar__label">Library</span>
|
|
16
|
+
* </a>
|
|
17
|
+
* <span class="juno-pillbar__sep"></span>
|
|
18
|
+
* <button class="juno-pillbar__item" aria-label="Sync">
|
|
19
|
+
* <svg class="juno-icon" aria-hidden="true"><use href="…#juno-i-arrows-clockwise" /></svg>
|
|
20
|
+
* </button>
|
|
21
|
+
* </nav>
|
|
22
|
+
* ════════════════════════════════════════════════════════════════════ */
|
|
23
|
+
|
|
24
|
+
.juno-pillbar {
|
|
25
|
+
--juno-role: var(--juno-active);
|
|
26
|
+
|
|
27
|
+
position: sticky;
|
|
28
|
+
inset-block-end: calc(var(--juno-space-16) + env(safe-area-inset-bottom, 0px));
|
|
29
|
+
z-index: var(--juno-z-raised);
|
|
30
|
+
margin-inline: auto;
|
|
31
|
+
margin-block-end: var(--juno-space-16);
|
|
32
|
+
inline-size: fit-content;
|
|
33
|
+
max-inline-size: calc(100% - 2 * var(--juno-space-16));
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
gap: var(--juno-space-2);
|
|
37
|
+
padding: var(--juno-space-4);
|
|
38
|
+
background: color-mix(in srgb, var(--juno-s1) 88%, transparent);
|
|
39
|
+
backdrop-filter: blur(12px);
|
|
40
|
+
border: var(--juno-border-width-1) solid var(--juno-border);
|
|
41
|
+
border-radius: 999px;
|
|
42
|
+
box-shadow: var(--juno-shadow-2);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.juno-pillbar__item {
|
|
46
|
+
appearance: none;
|
|
47
|
+
margin: 0;
|
|
48
|
+
border: none;
|
|
49
|
+
min-inline-size: var(--juno-size-tap-comfortable);
|
|
50
|
+
min-block-size: var(--juno-size-tap-comfortable);
|
|
51
|
+
display: inline-flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
justify-content: center;
|
|
54
|
+
gap: var(--juno-space-4);
|
|
55
|
+
padding-inline: var(--juno-space-10);
|
|
56
|
+
background: transparent;
|
|
57
|
+
border-radius: 999px;
|
|
58
|
+
font-family: var(--juno-font-family-sans);
|
|
59
|
+
font-size: var(--juno-font-size-10);
|
|
60
|
+
font-weight: var(--juno-font-weight-semibold);
|
|
61
|
+
letter-spacing: var(--juno-font-tracking-label);
|
|
62
|
+
text-transform: uppercase;
|
|
63
|
+
text-decoration: none;
|
|
64
|
+
color: var(--juno-label);
|
|
65
|
+
cursor: pointer;
|
|
66
|
+
transition:
|
|
67
|
+
color var(--juno-motion-duration-quick) var(--juno-motion-ease-standard),
|
|
68
|
+
background-color var(--juno-motion-duration-quick) var(--juno-motion-ease-standard);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.juno-pillbar__label {
|
|
72
|
+
max-inline-size: 12ch;
|
|
73
|
+
overflow: hidden;
|
|
74
|
+
text-overflow: ellipsis;
|
|
75
|
+
white-space: nowrap;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.juno-pillbar__item:hover { color: var(--juno-data); }
|
|
79
|
+
|
|
80
|
+
/* active = attribute, not class: aria-current (links) / aria-pressed (buttons) */
|
|
81
|
+
.juno-pillbar__item[aria-current],
|
|
82
|
+
.juno-pillbar__item[aria-pressed='true'] {
|
|
83
|
+
background: var(--juno-s3);
|
|
84
|
+
color: var(--juno-role);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.juno-pillbar__item:focus-visible {
|
|
88
|
+
outline: var(--juno-border-width-2) solid var(--juno-active);
|
|
89
|
+
outline-offset: calc(-1 * var(--juno-space-2));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.juno-pillbar__item:disabled {
|
|
93
|
+
opacity: var(--juno-opacity-disabled);
|
|
94
|
+
cursor: not-allowed;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.juno-pillbar__sep {
|
|
98
|
+
align-self: center;
|
|
99
|
+
inline-size: var(--juno-border-width-1);
|
|
100
|
+
block-size: var(--juno-space-20);
|
|
101
|
+
background: var(--juno-border);
|
|
102
|
+
flex-shrink: 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* reduced-transparency preference → solid surface, no blur */
|
|
106
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
107
|
+
.juno-pillbar {
|
|
108
|
+
background: var(--juno-s1);
|
|
109
|
+
backdrop-filter: none;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/* ════════════════════════════════════════════════════════════════════
|
|
2
|
+
* Component — Popover
|
|
3
|
+
* Anchored, click-triggered surface for rich, interactive content (filters,
|
|
4
|
+
* quick-edits, detail cards). Unlike a tooltip it holds focusable controls
|
|
5
|
+
* and persists until dismissed.
|
|
6
|
+
*
|
|
7
|
+
* Built on the native Popover API: the panel lives in the browser TOP LAYER,
|
|
8
|
+
* so it is never clipped by an ancestor's overflow/transform and never fights
|
|
9
|
+
* z-index. A `popovertarget` button is its implicit anchor; CSS anchor
|
|
10
|
+
* positioning pins it below, inline-end aligned. Open/close, light-dismiss,
|
|
11
|
+
* and ESC all come from the platform — zero JS.
|
|
12
|
+
* Usage:
|
|
13
|
+
* <button popovertarget="pf" aria-expanded="false">RANGE FILTER ▾</button>
|
|
14
|
+
* <div class="juno-popover" id="pf" popover role="dialog">
|
|
15
|
+
* …controls…
|
|
16
|
+
* <span class="juno-popover__arrow"></span>
|
|
17
|
+
* </div>
|
|
18
|
+
* (Older engines without anchor positioning fall back to the app setting
|
|
19
|
+
* inset on the `toggle` event — junoui ships the surface; the app may pin it.)
|
|
20
|
+
* ════════════════════════════════════════════════════════════════════ */
|
|
21
|
+
|
|
22
|
+
/* legacy grouping wrapper — kept for layout; positioning no longer needs it */
|
|
23
|
+
.juno-popover-anchor { position: relative; }
|
|
24
|
+
|
|
25
|
+
.juno-popover {
|
|
26
|
+
position: fixed;
|
|
27
|
+
margin: 0;
|
|
28
|
+
inset: auto;
|
|
29
|
+
position-area: block-end span-inline-start;
|
|
30
|
+
position-try-fallbacks: flip-block, flip-inline;
|
|
31
|
+
margin-block-start: var(--juno-space-12);
|
|
32
|
+
inline-size: 280px;
|
|
33
|
+
max-inline-size: calc(100vw - var(--juno-space-24));
|
|
34
|
+
background: var(--juno-s2);
|
|
35
|
+
color: var(--juno-data);
|
|
36
|
+
border: var(--juno-border-width-1) solid var(--juno-border);
|
|
37
|
+
border-radius: var(--juno-radius-5);
|
|
38
|
+
box-shadow: var(--juno-shadow-2);
|
|
39
|
+
padding: var(--juno-pad-surface-block) var(--juno-pad-surface-inline);
|
|
40
|
+
opacity: 0;
|
|
41
|
+
transform: translateY(calc(-1 * var(--juno-space-4)));
|
|
42
|
+
transition:
|
|
43
|
+
opacity var(--juno-motion-duration-quick) var(--juno-motion-ease-decel),
|
|
44
|
+
transform var(--juno-motion-duration-quick) var(--juno-motion-ease-decel),
|
|
45
|
+
overlay var(--juno-motion-duration-quick) allow-discrete,
|
|
46
|
+
display var(--juno-motion-duration-quick) allow-discrete;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.juno-popover:popover-open {
|
|
50
|
+
opacity: 1;
|
|
51
|
+
transform: none;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@starting-style {
|
|
55
|
+
.juno-popover:popover-open {
|
|
56
|
+
opacity: 0;
|
|
57
|
+
transform: translateY(calc(-1 * var(--juno-space-4)));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* arrow back to the anchor (top-end corner) */
|
|
62
|
+
.juno-popover__arrow {
|
|
63
|
+
position: absolute;
|
|
64
|
+
inset-block-start: calc(-1 * var(--juno-space-4) - var(--juno-border-width-1));
|
|
65
|
+
inset-inline-end: var(--juno-space-24);
|
|
66
|
+
inline-size: var(--juno-space-8);
|
|
67
|
+
block-size: var(--juno-space-8);
|
|
68
|
+
background: var(--juno-s2);
|
|
69
|
+
border: var(--juno-border-width-1) solid var(--juno-border);
|
|
70
|
+
border-block-end: none;
|
|
71
|
+
border-inline-end: none;
|
|
72
|
+
transform: rotate(45deg);
|
|
73
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/* ════════════════════════════════════════════════════════════════════
|
|
2
|
+
* Component — Rail (app-shell side navigation)
|
|
3
|
+
* A collapsible icon-and-label nav column: the left edge of the standard
|
|
4
|
+
* junoui app shell (see docs/layout.md#app-shell). Zero JS here — the
|
|
5
|
+
* app toggles .juno-rail--collapsed and sets aria-current="page"; the
|
|
6
|
+
* active item is styled off the attribute, not a class.
|
|
7
|
+
* Usage:
|
|
8
|
+
* <nav class="juno-rail" aria-label="Primary">
|
|
9
|
+
* <div class="juno-rail__brand">JUNO</div>
|
|
10
|
+
* <a class="juno-rail__item" href="…" aria-current="page">
|
|
11
|
+
* <svg class="juno-icon"><use href="…#juno-i-squares-four" /></svg>
|
|
12
|
+
* <span class="juno-rail__label">Library</span>
|
|
13
|
+
* </a>
|
|
14
|
+
* </nav>
|
|
15
|
+
* ════════════════════════════════════════════════════════════════════ */
|
|
16
|
+
|
|
17
|
+
.juno-rail {
|
|
18
|
+
--juno-role: var(--juno-active);
|
|
19
|
+
--juno-rail-width: 180px;
|
|
20
|
+
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
gap: var(--juno-space-2);
|
|
24
|
+
inline-size: var(--juno-rail-width);
|
|
25
|
+
min-block-size: 100%;
|
|
26
|
+
padding-block: var(--juno-space-8);
|
|
27
|
+
background: var(--juno-s1);
|
|
28
|
+
border-inline-end: var(--juno-border-width-1) solid var(--juno-border);
|
|
29
|
+
transition: inline-size var(--juno-motion-duration-base) var(--juno-motion-ease-standard);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.juno-rail__brand {
|
|
33
|
+
display: flex;
|
|
34
|
+
align-items: center;
|
|
35
|
+
gap: var(--juno-gap-control);
|
|
36
|
+
padding: var(--juno-space-8) var(--juno-space-16);
|
|
37
|
+
margin-block-end: var(--juno-space-8);
|
|
38
|
+
font-family: var(--juno-font-family-mono);
|
|
39
|
+
font-size: var(--juno-font-size-13);
|
|
40
|
+
font-weight: var(--juno-font-weight-bold);
|
|
41
|
+
letter-spacing: var(--juno-font-tracking-wide);
|
|
42
|
+
text-transform: uppercase;
|
|
43
|
+
color: var(--juno-data);
|
|
44
|
+
white-space: nowrap;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.juno-rail__item {
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
gap: var(--juno-gap-control);
|
|
51
|
+
padding: var(--juno-space-8) var(--juno-space-16);
|
|
52
|
+
border-inline-start: var(--juno-border-width-2) solid transparent;
|
|
53
|
+
font-family: var(--juno-font-family-sans);
|
|
54
|
+
font-size: var(--juno-font-size-12);
|
|
55
|
+
font-weight: var(--juno-font-weight-semibold);
|
|
56
|
+
letter-spacing: var(--juno-font-tracking-label);
|
|
57
|
+
text-transform: uppercase;
|
|
58
|
+
text-decoration: none;
|
|
59
|
+
color: var(--juno-label);
|
|
60
|
+
white-space: nowrap;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.juno-rail__item:hover {
|
|
65
|
+
background: var(--juno-s2);
|
|
66
|
+
color: var(--juno-data);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* active = attribute, not class: <a aria-current="page"> */
|
|
70
|
+
.juno-rail__item[aria-current] {
|
|
71
|
+
background: var(--juno-s3);
|
|
72
|
+
border-inline-start-color: var(--juno-role);
|
|
73
|
+
color: var(--juno-data);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.juno-rail__item:focus-visible {
|
|
77
|
+
outline: var(--juno-border-width-2) solid var(--juno-active);
|
|
78
|
+
outline-offset: calc(-1 * var(--juno-space-2));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Collapsed — icons only; labels leave the accessibility tree too, so
|
|
82
|
+
give collapsed items a title or aria-label from the app. */
|
|
83
|
+
.juno-rail--collapsed {
|
|
84
|
+
--juno-rail-width: var(--juno-space-56);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.juno-rail--collapsed .juno-rail__label,
|
|
88
|
+
.juno-rail--collapsed .juno-rail__brand .juno-rail__label {
|
|
89
|
+
display: none;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.juno-rail--collapsed .juno-rail__item,
|
|
93
|
+
.juno-rail--collapsed .juno-rail__brand {
|
|
94
|
+
justify-content: center;
|
|
95
|
+
padding-inline: 0;
|
|
96
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/* ════════════════════════════════════════════════════════════════════
|
|
2
|
+
* Component — Data readout tile
|
|
3
|
+
* Usage:
|
|
4
|
+
* <div class="juno-readout juno--nominal">
|
|
5
|
+
* <span class="juno-readout__label">N1</span>
|
|
6
|
+
* <span class="juno-readout__value">89.3</span>
|
|
7
|
+
* <span class="juno-readout__unit">%</span>
|
|
8
|
+
* </div>
|
|
9
|
+
* The value takes --juno-role; default role is the neutral data color.
|
|
10
|
+
* ════════════════════════════════════════════════════════════════════ */
|
|
11
|
+
|
|
12
|
+
.juno-readout {
|
|
13
|
+
--juno-role: var(--juno-data);
|
|
14
|
+
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
background: var(--juno-s2);
|
|
18
|
+
border-radius: var(--juno-radius-5);
|
|
19
|
+
padding: var(--juno-space-20) var(--juno-space-16);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.juno-readout--alert { border: var(--juno-border-width-1) solid var(--juno-role); }
|
|
23
|
+
|
|
24
|
+
.juno-readout__label {
|
|
25
|
+
font-size: var(--juno-font-size-10);
|
|
26
|
+
letter-spacing: var(--juno-font-tracking-wider);
|
|
27
|
+
text-transform: uppercase;
|
|
28
|
+
color: var(--juno-label);
|
|
29
|
+
margin-bottom: var(--juno-space-8);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.juno-readout__value {
|
|
33
|
+
font-family: var(--juno-font-family-mono);
|
|
34
|
+
font-weight: var(--juno-font-weight-bold);
|
|
35
|
+
font-size: var(--juno-font-size-38);
|
|
36
|
+
line-height: var(--juno-font-lineHeight-none);
|
|
37
|
+
color: var(--juno-role);
|
|
38
|
+
font-variant-numeric: tabular-nums;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.juno-readout__unit {
|
|
42
|
+
font-size: var(--juno-font-size-11);
|
|
43
|
+
color: var(--juno-label);
|
|
44
|
+
margin-top: var(--juno-space-4);
|
|
45
|
+
}
|