@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.
Files changed (136) hide show
  1. package/CHANGELOG.md +199 -0
  2. package/README.md +117 -0
  3. package/dist/android/colors.xml +94 -0
  4. package/dist/android/dimens.xml +49 -0
  5. package/dist/css/juno-fonts.css +45 -0
  6. package/dist/css/juno-tokens.css +345 -0
  7. package/dist/css/juno.css +4481 -0
  8. package/dist/flutter/juno_tokens.dart +143 -0
  9. package/dist/fonts/b612-400.woff2 +0 -0
  10. package/dist/fonts/b612-700.woff2 +0 -0
  11. package/dist/fonts/b612-mono-400.woff2 +0 -0
  12. package/dist/fonts/b612-mono-700.woff2 +0 -0
  13. package/dist/icons/juno-icons.svg +69 -0
  14. package/dist/ios/JunoTokens.swift +149 -0
  15. package/dist/js/tokens.d.ts +11 -0
  16. package/dist/js/tokens.js +244 -0
  17. package/dist/json/tokens.json +921 -0
  18. package/dist/scss/_juno-tokens.scss +176 -0
  19. package/package.json +92 -0
  20. package/src/css/base.css +120 -0
  21. package/src/css/components/accordion.css +83 -0
  22. package/src/css/components/alert.css +90 -0
  23. package/src/css/components/avatar.css +82 -0
  24. package/src/css/components/badge.css +55 -0
  25. package/src/css/components/breadcrumb.css +64 -0
  26. package/src/css/components/button.css +59 -0
  27. package/src/css/components/card.css +68 -0
  28. package/src/css/components/checkbox.css +89 -0
  29. package/src/css/components/chip.css +89 -0
  30. package/src/css/components/divider.css +50 -0
  31. package/src/css/components/dock.css +74 -0
  32. package/src/css/components/dot.css +35 -0
  33. package/src/css/components/drawer.css +80 -0
  34. package/src/css/components/field.css +37 -0
  35. package/src/css/components/gauge.css +78 -0
  36. package/src/css/components/icon.css +35 -0
  37. package/src/css/components/input.css +60 -0
  38. package/src/css/components/list.css +135 -0
  39. package/src/css/components/loader.css +159 -0
  40. package/src/css/components/menu.css +114 -0
  41. package/src/css/components/modal.css +161 -0
  42. package/src/css/components/navbar.css +103 -0
  43. package/src/css/components/pagination.css +72 -0
  44. package/src/css/components/pillbar.css +111 -0
  45. package/src/css/components/popover.css +73 -0
  46. package/src/css/components/rail.css +96 -0
  47. package/src/css/components/readout.css +45 -0
  48. package/src/css/components/segmented.css +99 -0
  49. package/src/css/components/select.css +38 -0
  50. package/src/css/components/skeleton.css +62 -0
  51. package/src/css/components/slider.css +117 -0
  52. package/src/css/components/spark.css +26 -0
  53. package/src/css/components/stepper.css +121 -0
  54. package/src/css/components/switch.css +131 -0
  55. package/src/css/components/table.css +443 -0
  56. package/src/css/components/tabs.css +66 -0
  57. package/src/css/components/thumb.css +69 -0
  58. package/src/css/components/toast.css +109 -0
  59. package/src/css/components/toggle-button.css +91 -0
  60. package/src/css/components/tooltip.css +155 -0
  61. package/src/css/density.css +40 -0
  62. package/src/css/fonts.css +45 -0
  63. package/src/css/layout.css +99 -0
  64. package/src/css/typescale.css +27 -0
  65. package/src/css/utilities.css +76 -0
  66. package/src/fonts/LICENSE +93 -0
  67. package/src/fonts/b612-400.woff2 +0 -0
  68. package/src/fonts/b612-700.woff2 +0 -0
  69. package/src/fonts/b612-mono-400.woff2 +0 -0
  70. package/src/fonts/b612-mono-700.woff2 +0 -0
  71. package/src/icons/LICENSE +21 -0
  72. package/src/icons/arrow-clockwise.svg +1 -0
  73. package/src/icons/arrow-down.svg +1 -0
  74. package/src/icons/arrow-left.svg +1 -0
  75. package/src/icons/arrow-right.svg +1 -0
  76. package/src/icons/arrow-up.svg +1 -0
  77. package/src/icons/arrows-clockwise.svg +1 -0
  78. package/src/icons/arrows-out.svg +1 -0
  79. package/src/icons/bell.svg +1 -0
  80. package/src/icons/calendar-blank.svg +1 -0
  81. package/src/icons/caret-down.svg +1 -0
  82. package/src/icons/caret-left.svg +1 -0
  83. package/src/icons/caret-right.svg +1 -0
  84. package/src/icons/caret-up.svg +1 -0
  85. package/src/icons/chat-circle.svg +1 -0
  86. package/src/icons/check-circle.svg +1 -0
  87. package/src/icons/check.svg +1 -0
  88. package/src/icons/clock.svg +1 -0
  89. package/src/icons/cloud-arrow-down.svg +1 -0
  90. package/src/icons/cloud.svg +1 -0
  91. package/src/icons/copy.svg +1 -0
  92. package/src/icons/cpu.svg +1 -0
  93. package/src/icons/dots-three-vertical.svg +1 -0
  94. package/src/icons/dots-three.svg +1 -0
  95. package/src/icons/download.svg +1 -0
  96. package/src/icons/envelope.svg +1 -0
  97. package/src/icons/eye-slash.svg +1 -0
  98. package/src/icons/eye.svg +1 -0
  99. package/src/icons/file.svg +1 -0
  100. package/src/icons/film-strip.svg +1 -0
  101. package/src/icons/floppy-disk.svg +1 -0
  102. package/src/icons/folder.svg +1 -0
  103. package/src/icons/funnel.svg +1 -0
  104. package/src/icons/gear.svg +1 -0
  105. package/src/icons/hard-drives.svg +1 -0
  106. package/src/icons/heart.svg +1 -0
  107. package/src/icons/hexagon.svg +1 -0
  108. package/src/icons/house.svg +1 -0
  109. package/src/icons/images.svg +1 -0
  110. package/src/icons/info.svg +1 -0
  111. package/src/icons/link.svg +1 -0
  112. package/src/icons/list.svg +1 -0
  113. package/src/icons/lock-open.svg +1 -0
  114. package/src/icons/lock.svg +1 -0
  115. package/src/icons/magnifying-glass.svg +1 -0
  116. package/src/icons/minus.svg +1 -0
  117. package/src/icons/moon.svg +1 -0
  118. package/src/icons/pencil-simple.svg +1 -0
  119. package/src/icons/play.svg +1 -0
  120. package/src/icons/plus.svg +1 -0
  121. package/src/icons/puzzle-piece.svg +1 -0
  122. package/src/icons/question.svg +1 -0
  123. package/src/icons/sliders-horizontal.svg +1 -0
  124. package/src/icons/sliders.svg +1 -0
  125. package/src/icons/squares-four.svg +1 -0
  126. package/src/icons/star.svg +1 -0
  127. package/src/icons/sun.svg +1 -0
  128. package/src/icons/trash.svg +1 -0
  129. package/src/icons/upload-simple.svg +1 -0
  130. package/src/icons/upload.svg +1 -0
  131. package/src/icons/user.svg +1 -0
  132. package/src/icons/users.svg +1 -0
  133. package/src/icons/warning-circle.svg +1 -0
  134. package/src/icons/warning.svg +1 -0
  135. package/src/icons/x-circle.svg +1 -0
  136. package/src/icons/x.svg +1 -0
@@ -0,0 +1,99 @@
1
+ /* ════════════════════════════════════════════════════════════════════
2
+ * Component — Segmented control
3
+ * Exclusive-choice pill row (filters, mode switchers, sort pickers).
4
+ * Zero JS: native radios carry the state; the checked pill takes the
5
+ * role color (default label-neutral; add .juno--<role> to recolor).
6
+ * Usage:
7
+ * <fieldset class="juno-seg juno--active">
8
+ * <label class="juno-seg__opt"><input type="radio" name="kind" checked /><span>ALL</span></label>
9
+ * <label class="juno-seg__opt"><input type="radio" name="kind" /><span>PHOTOS</span></label>
10
+ * </fieldset>
11
+ * JS-driven apps may use buttons instead; the app flips aria-pressed:
12
+ * <div class="juno-seg" role="group">
13
+ * <button class="juno-seg__opt" aria-pressed="true">ALL</button>
14
+ * </div>
15
+ * ════════════════════════════════════════════════════════════════════ */
16
+
17
+ .juno-seg {
18
+ --juno-role: var(--juno-data);
19
+
20
+ display: inline-flex;
21
+ flex-wrap: wrap;
22
+ gap: var(--juno-space-4);
23
+ margin: 0;
24
+ padding: 0;
25
+ border: 0;
26
+ }
27
+
28
+ .juno-seg__opt {
29
+ display: inline-flex;
30
+ cursor: pointer;
31
+ }
32
+
33
+ /* visually hidden but still focusable + in the tab order */
34
+ .juno-seg__opt input {
35
+ position: absolute;
36
+ inline-size: 1px;
37
+ block-size: 1px;
38
+ opacity: 0;
39
+ }
40
+
41
+ /* the pill — a span after the radio, or the aria-pressed button itself */
42
+ .juno-seg__opt input + span,
43
+ button.juno-seg__opt {
44
+ display: inline-flex;
45
+ align-items: center;
46
+ gap: var(--juno-gap-control);
47
+ font-family: var(--juno-font-family-sans);
48
+ font-size: var(--juno-font-size-11);
49
+ font-weight: var(--juno-font-weight-bold);
50
+ letter-spacing: var(--juno-font-tracking-label);
51
+ text-transform: uppercase;
52
+ padding: var(--juno-space-4) var(--juno-space-10);
53
+ color: var(--juno-label);
54
+ background: transparent;
55
+ border: var(--juno-border-width-1) solid var(--juno-border);
56
+ border-radius: var(--juno-radius-3);
57
+ transition:
58
+ color var(--juno-motion-duration-quick) var(--juno-motion-ease-standard),
59
+ border-color var(--juno-motion-duration-quick) var(--juno-motion-ease-standard),
60
+ background-color var(--juno-motion-duration-quick) var(--juno-motion-ease-standard);
61
+ }
62
+
63
+ .juno-seg__opt:hover input:not(:checked) + span,
64
+ button.juno-seg__opt[aria-pressed='false']:hover {
65
+ background: var(--juno-s2);
66
+ color: var(--juno-data);
67
+ }
68
+
69
+ .juno-seg__opt input:checked + span,
70
+ button.juno-seg__opt[aria-pressed='true'] {
71
+ background: var(--juno-s3);
72
+ border-color: var(--juno-role);
73
+ color: var(--juno-role);
74
+ }
75
+
76
+ .juno-seg__opt input:focus-visible + span,
77
+ button.juno-seg__opt:focus-visible {
78
+ outline: var(--juno-border-width-2) solid var(--juno-active);
79
+ outline-offset: var(--juno-space-2);
80
+ }
81
+
82
+ .juno-seg__opt input:disabled + span,
83
+ button.juno-seg__opt:disabled {
84
+ opacity: var(--juno-opacity-disabled);
85
+ cursor: not-allowed;
86
+ }
87
+
88
+ .juno-seg:disabled .juno-seg__opt,
89
+ .juno-seg__opt:has(:disabled) {
90
+ cursor: not-allowed;
91
+ }
92
+
93
+ /* Small — matches .juno-btn--sm; for dense toolbars */
94
+ .juno-seg--sm .juno-seg__opt input + span,
95
+ .juno-seg--sm > button.juno-seg__opt {
96
+ font-size: var(--juno-font-size-10);
97
+ letter-spacing: var(--juno-font-tracking-wide);
98
+ padding: var(--juno-space-2) var(--juno-space-8);
99
+ }
@@ -0,0 +1,38 @@
1
+ /* ════════════════════════════════════════════════════════════════════
2
+ * Component — Select
3
+ * A native <select> styled as an input, with a token-colored chevron.
4
+ * Usage:
5
+ * <div class="juno-select">
6
+ * <select class="juno-input">
7
+ * <option>NOMINAL</option><option>CAUTION</option>
8
+ * </select>
9
+ * </div>
10
+ * The inner <select> reuses .juno-input for the box; the wrapper adds the
11
+ * chevron and reserves room for it (inline-end, so it mirrors under RTL).
12
+ * ════════════════════════════════════════════════════════════════════ */
13
+
14
+ .juno-select {
15
+ position: relative;
16
+ display: block;
17
+ }
18
+
19
+ .juno-select > .juno-input {
20
+ appearance: none;
21
+ padding-inline-end: var(--juno-space-32);
22
+ font-family: var(--juno-font-family-sans);
23
+ cursor: pointer;
24
+ }
25
+
26
+ /* chevron — CSS triangle so it inherits a token color, no SVG asset */
27
+ .juno-select::after {
28
+ content: '';
29
+ position: absolute;
30
+ inset-inline-end: var(--juno-space-12);
31
+ inset-block-start: 50%;
32
+ inline-size: 0;
33
+ block-size: 0;
34
+ border-inline: var(--juno-space-4) solid transparent;
35
+ border-block-start: var(--juno-space-4) solid var(--juno-label);
36
+ transform: translateY(-25%);
37
+ pointer-events: none;
38
+ }
@@ -0,0 +1,62 @@
1
+ /* ════════════════════════════════════════════════════════════════════
2
+ * Component — Skeleton · loading placeholder
3
+ * A shimmering block that stands in for content not yet loaded. Same
4
+ * shimmer language as the table's row skeleton, generalized: text lines,
5
+ * blocks, and circles (avatars). Zero JS — size it with width / height or
6
+ * --juno-skeleton-h. Honors prefers-reduced-motion (shimmer → static).
7
+ * Mark the loading region aria-busy="true" so AT announces it.
8
+ * Usage:
9
+ * <div aria-busy="true" aria-live="polite">
10
+ * <span class="juno-skeleton juno-skeleton--circle" style="--juno-skeleton-h: 48px;"></span>
11
+ * <span class="juno-skeleton juno-skeleton--text" style="inline-size: 60%;"></span>
12
+ * <span class="juno-skeleton juno-skeleton--text"></span>
13
+ * </div>
14
+ * ════════════════════════════════════════════════════════════════════ */
15
+
16
+ .juno-skeleton {
17
+ --juno-skeleton-h: var(--juno-space-16);
18
+
19
+ display: block;
20
+ inline-size: 100%;
21
+ block-size: var(--juno-skeleton-h);
22
+ border-radius: var(--juno-radius-3);
23
+ background: linear-gradient(90deg, var(--juno-s2) 0%, var(--juno-s3) 50%, var(--juno-s2) 100%);
24
+ background-size: 200% 100%;
25
+ animation: juno-skeleton-shimmer 1.2s ease-in-out infinite;
26
+ }
27
+
28
+ /* a text line — caps height to one line, soft radius */
29
+ .juno-skeleton--text {
30
+ --juno-skeleton-h: 1em;
31
+
32
+ border-radius: var(--juno-radius-2);
33
+ }
34
+
35
+ /* round avatar / icon placeholder — keep it square via the inline size */
36
+ .juno-skeleton--circle {
37
+ inline-size: var(--juno-skeleton-h);
38
+ border-radius: 50%;
39
+ }
40
+
41
+ /* a larger surface (card / media) */
42
+ .juno-skeleton--block {
43
+ --juno-skeleton-h: var(--juno-space-56);
44
+
45
+ border-radius: var(--juno-radius-4);
46
+ }
47
+
48
+ @keyframes juno-skeleton-shimmer {
49
+ from {
50
+ background-position: 200% 0;
51
+ }
52
+
53
+ to {
54
+ background-position: -200% 0;
55
+ }
56
+ }
57
+
58
+ @media (prefers-reduced-motion: reduce) {
59
+ .juno-skeleton {
60
+ animation: none;
61
+ }
62
+ }
@@ -0,0 +1,117 @@
1
+ /* ════════════════════════════════════════════════════════════════════
2
+ * Component — Slider · track + grip lever
3
+ * A recessed channel with detent notches, an accent fill, and a gripped
4
+ * lever. Value-driven: the app sets --juno-slider-pct (0–100) and owns
5
+ * drag / keyboard / ARIA (role="slider" + aria-value*), the same split as
6
+ * the overlays. Two sizes: standard (lever always full) and --compact
7
+ * (lever recedes to a slim marker until hover / focus / drag).
8
+ * Usage:
9
+ * <div class="juno-slider" style="--juno-slider-pct: 72" role="slider"
10
+ * tabindex="0" aria-valuenow="72" aria-valuemin="0" aria-valuemax="100">
11
+ * <span class="juno-slider__channel"></span>
12
+ * <span class="juno-slider__lever"></span>
13
+ * </div>
14
+ * ════════════════════════════════════════════════════════════════════ */
15
+
16
+ .juno-slider {
17
+ --juno-role: var(--juno-active);
18
+ --juno-slider-pct: 0;
19
+
20
+ position: relative;
21
+ display: flex;
22
+ align-items: center;
23
+ inline-size: 100%;
24
+ block-size: 48px;
25
+ cursor: pointer;
26
+ touch-action: none;
27
+ }
28
+
29
+ .juno-slider__channel {
30
+ position: absolute;
31
+ inset-inline: 0;
32
+ inset-block-start: 50%;
33
+ transform: translateY(-50%);
34
+ block-size: var(--juno-space-20);
35
+ background-color: var(--juno-s0);
36
+ border: var(--juno-border-width-1) solid var(--juno-control-edge-strong);
37
+ border-radius: var(--juno-radius-4);
38
+ box-shadow: inset 0 2px 4px rgb(0 0 0 / 0.5);
39
+ overflow: hidden;
40
+ }
41
+
42
+ /* detent notches every 10% */
43
+ .juno-slider__channel::before {
44
+ content: '';
45
+ position: absolute;
46
+ inset: 0;
47
+ background-image: repeating-linear-gradient(
48
+ 90deg,
49
+ transparent 0 calc(10% - 1px),
50
+ var(--juno-muted) calc(10% - 1px),
51
+ var(--juno-muted) 10%
52
+ );
53
+ opacity: 0.5;
54
+ }
55
+
56
+ /* accent fill up to the value */
57
+ .juno-slider__channel::after {
58
+ content: '';
59
+ position: absolute;
60
+ inset-block: 0;
61
+ inset-inline-start: 0;
62
+ inline-size: calc(var(--juno-slider-pct) * 1%);
63
+ background: linear-gradient(
64
+ 90deg,
65
+ color-mix(in srgb, var(--juno-role) 8%, transparent),
66
+ color-mix(in srgb, var(--juno-role) 34%, transparent)
67
+ );
68
+ }
69
+
70
+ /* gripped lever, positioned at the value */
71
+ .juno-slider__lever {
72
+ position: absolute;
73
+ inset-inline-start: calc(var(--juno-slider-pct) * 1%);
74
+ inset-block-start: 50%;
75
+ transform: translate(-50%, -50%);
76
+ inline-size: 26px;
77
+ block-size: 40px;
78
+ border-radius: var(--juno-radius-4);
79
+ border: var(--juno-border-width-1) solid var(--juno-knob-edge);
80
+ background:
81
+ repeating-linear-gradient(90deg, var(--juno-knob-grip) 0 2px, transparent 2px 5px) center / 12px
82
+ 16px no-repeat,
83
+ linear-gradient(180deg, var(--juno-knob-face-hi), var(--juno-knob-face-lo));
84
+ box-shadow: 0 2px 4px rgb(0 0 0 / 0.5);
85
+ pointer-events: none;
86
+ transition:
87
+ inline-size var(--juno-motion-duration-base) ease,
88
+ block-size var(--juno-motion-duration-base) ease,
89
+ box-shadow var(--juno-motion-duration-base) ease;
90
+ }
91
+
92
+ /* compact — lever minimizes to a slim marker until engaged */
93
+ .juno-slider--compact .juno-slider__lever {
94
+ inline-size: 10px;
95
+ block-size: 22px;
96
+ border-radius: var(--juno-radius-3);
97
+ background: linear-gradient(180deg, var(--juno-knob-face-hi), var(--juno-knob-face-lo));
98
+ box-shadow: 0 1px 2px rgb(0 0 0 / 0.45);
99
+ }
100
+
101
+ .juno-slider--compact:hover .juno-slider__lever,
102
+ .juno-slider--compact:focus-visible .juno-slider__lever,
103
+ .juno-slider--compact:active .juno-slider__lever {
104
+ inline-size: 26px;
105
+ block-size: 40px;
106
+ border-radius: var(--juno-radius-4);
107
+ background:
108
+ repeating-linear-gradient(90deg, var(--juno-knob-grip) 0 2px, transparent 2px 5px) center / 12px
109
+ 16px no-repeat,
110
+ linear-gradient(180deg, var(--juno-knob-face-hi), var(--juno-knob-face-lo));
111
+ box-shadow: 0 2px 4px rgb(0 0 0 / 0.5);
112
+ }
113
+
114
+ .juno-slider[aria-disabled='true'] {
115
+ opacity: var(--juno-opacity-disabled);
116
+ cursor: not-allowed;
117
+ }
@@ -0,0 +1,26 @@
1
+ /* ════════════════════════════════════════════════════════════════════
2
+ * Component — Spark (sparkline contract)
3
+ * junoui ships no charting: the app generates the <polyline> points;
4
+ * this class supplies the size / stroke / role-color contract so every
5
+ * sparkline matches. Generator recipe: docs/components/spark.md.
6
+ * Usage:
7
+ * <svg class="juno-spark juno--nominal" viewBox="0 0 120 24" preserveAspectRatio="none">
8
+ * <polyline points="0,20 8,14 16,17 …" />
9
+ * </svg>
10
+ * ════════════════════════════════════════════════════════════════════ */
11
+
12
+ .juno-spark {
13
+ --juno-role: var(--juno-active);
14
+
15
+ display: block;
16
+ inline-size: 100%;
17
+ block-size: var(--juno-space-24);
18
+ }
19
+
20
+ .juno-spark polyline {
21
+ fill: none;
22
+ stroke: var(--juno-role);
23
+ stroke-width: 1.5;
24
+ stroke-linejoin: round;
25
+ stroke-linecap: round;
26
+ }
@@ -0,0 +1,121 @@
1
+ /* ════════════════════════════════════════════════════════════════════
2
+ * Component — Stepper · multi-step progress
3
+ * A horizontal sequence of steps with numbered markers and connectors.
4
+ * Each step carries data-state: complete · current · upcoming. Mark the
5
+ * active step aria-current="step". Built on a real <ol>; the app sets the
6
+ * states. Zero JS. Reads --juno-role (default active) for the accent.
7
+ * Usage:
8
+ * <ol class="juno-stepper">
9
+ * <li class="juno-stepper__step" data-state="complete">
10
+ * <span class="juno-stepper__marker">
11
+ * <svg class="juno-icon juno-icon--sm"><use href="…#juno-i-check" /></svg>
12
+ * </span>
13
+ * <span class="juno-stepper__label">Plan</span>
14
+ * </li>
15
+ * <li class="juno-stepper__step" data-state="current" aria-current="step">
16
+ * <span class="juno-stepper__marker">2</span>
17
+ * <span class="juno-stepper__label">Build</span>
18
+ * </li>
19
+ * <li class="juno-stepper__step" data-state="upcoming">
20
+ * <span class="juno-stepper__marker">3</span>
21
+ * <span class="juno-stepper__label">Ship</span>
22
+ * </li>
23
+ * </ol>
24
+ * ════════════════════════════════════════════════════════════════════ */
25
+
26
+ .juno-stepper {
27
+ --juno-stepper-marker: var(--juno-space-28);
28
+ --juno-role: var(--juno-active);
29
+
30
+ display: flex;
31
+ margin: 0;
32
+ padding: 0;
33
+ list-style: none;
34
+ }
35
+
36
+ .juno-stepper__step {
37
+ position: relative;
38
+ flex: 1;
39
+ display: flex;
40
+ flex-direction: column;
41
+ align-items: center;
42
+ gap: var(--juno-space-8);
43
+ text-align: center;
44
+ }
45
+
46
+ /* connector — a line from the previous marker into this one */
47
+ .juno-stepper__step:not(:first-child)::before {
48
+ content: '';
49
+ position: absolute;
50
+ inset-block-start: calc(var(--juno-stepper-marker) / 2);
51
+ inset-inline-end: 50%;
52
+ inline-size: 100%;
53
+ block-size: var(--juno-border-width-1);
54
+ background: var(--juno-muted);
55
+ }
56
+
57
+ /* a reached step (complete or current) lights its incoming connector */
58
+ .juno-stepper__step[data-state='complete']::before,
59
+ .juno-stepper__step[data-state='current']::before {
60
+ background: var(--juno-role);
61
+ }
62
+
63
+ .juno-stepper__marker {
64
+ position: relative;
65
+ z-index: 1;
66
+ display: grid;
67
+ place-content: center;
68
+ inline-size: var(--juno-stepper-marker);
69
+ block-size: var(--juno-stepper-marker);
70
+ border-radius: 50%;
71
+ border: var(--juno-border-width-1) solid var(--juno-control-edge-strong);
72
+ background: var(--juno-control-surface);
73
+ color: var(--juno-label);
74
+ font-family: var(--juno-font-family-mono);
75
+ font-size: var(--juno-font-size-12);
76
+ font-weight: var(--juno-font-weight-bold);
77
+ }
78
+
79
+ .juno-stepper__step[data-state='complete'] .juno-stepper__marker {
80
+ border-color: var(--juno-role);
81
+ background: color-mix(in srgb, var(--juno-role) 18%, var(--juno-control-surface));
82
+ color: var(--juno-role);
83
+ }
84
+
85
+ .juno-stepper__step[data-state='current'] .juno-stepper__marker {
86
+ border-color: var(--juno-role);
87
+ color: var(--juno-role);
88
+ box-shadow: 0 0 0 var(--juno-border-width-2) color-mix(in srgb, var(--juno-role) 30%, transparent);
89
+ }
90
+
91
+ .juno-stepper__label {
92
+ max-inline-size: 14ch;
93
+ color: var(--juno-label);
94
+ font-family: var(--juno-font-family-sans);
95
+ font-size: var(--juno-font-size-12);
96
+ }
97
+
98
+ .juno-stepper__step[data-state='complete'] .juno-stepper__label,
99
+ .juno-stepper__step[data-state='current'] .juno-stepper__label {
100
+ color: var(--juno-data);
101
+ }
102
+
103
+ /* vertical — steps stack; connector runs down the marker column */
104
+ .juno-stepper--vertical {
105
+ flex-direction: column;
106
+ }
107
+
108
+ .juno-stepper--vertical .juno-stepper__step {
109
+ flex-direction: row;
110
+ align-items: flex-start;
111
+ gap: var(--juno-space-12);
112
+ text-align: start;
113
+ padding-block-end: var(--juno-space-16);
114
+ }
115
+
116
+ .juno-stepper--vertical .juno-stepper__step:not(:first-child)::before {
117
+ inset-block: calc(var(--juno-stepper-marker) / -2) auto;
118
+ inset-inline: calc(var(--juno-stepper-marker) / 2) auto;
119
+ inline-size: var(--juno-border-width-1);
120
+ block-size: 100%;
121
+ }
@@ -0,0 +1,131 @@
1
+ /* ════════════════════════════════════════════════════════════════════
2
+ * Component — Switch (toggle) · labeled rocker
3
+ * A wide squared slide-switch with the state printed inside the track
4
+ * (ON / OFF, mono) and a gripped knob; lit nominal-green when ON. A real
5
+ * checkbox drives it; the app owns state. Honors prefers-reduced-motion.
6
+ * Usage:
7
+ * <label class="juno-switch">
8
+ * <input class="juno-switch__input" type="checkbox" role="switch" />
9
+ * <span class="juno-switch__track"></span>
10
+ * <span>AUTO-SCALING</span>
11
+ * </label>
12
+ * ════════════════════════════════════════════════════════════════════ */
13
+
14
+ .juno-switch {
15
+ display: inline-flex;
16
+ align-items: center;
17
+ gap: var(--juno-gap-control);
18
+ font-family: var(--juno-font-family-sans);
19
+ font-size: var(--juno-font-size-13);
20
+ color: var(--juno-data);
21
+ cursor: pointer;
22
+ }
23
+
24
+ /* visually hidden but still focusable + in the tab order */
25
+ .juno-switch__input {
26
+ position: absolute;
27
+ inline-size: 1px;
28
+ block-size: 1px;
29
+ opacity: 0;
30
+ }
31
+
32
+ .juno-switch__track {
33
+ --juno-role: var(--juno-nominal);
34
+
35
+ position: relative;
36
+ flex-shrink: 0;
37
+ inline-size: 70px;
38
+ block-size: var(--juno-space-28);
39
+ background: var(--juno-control-surface);
40
+ border: var(--juno-border-width-1) solid var(--juno-control-edge-strong);
41
+ border-radius: var(--juno-radius-4);
42
+ box-shadow: inset 0 1px 2px rgb(0 0 0 / 0.4);
43
+ transition:
44
+ border-color var(--juno-motion-duration-quick) var(--juno-motion-ease-standard),
45
+ background-color var(--juno-motion-duration-quick) var(--juno-motion-ease-standard);
46
+ }
47
+
48
+ /* ON / OFF legend, printed opposite the knob */
49
+ .juno-switch__track::after {
50
+ content: 'OFF';
51
+ position: absolute;
52
+ inset-block: 0;
53
+ inset-inline-end: var(--juno-space-8);
54
+ display: flex;
55
+ align-items: center;
56
+ font-family: var(--juno-font-family-mono);
57
+ font-size: var(--juno-font-size-10);
58
+ font-weight: var(--juno-font-weight-bold);
59
+ letter-spacing: var(--juno-font-tracking-wide);
60
+ color: var(--juno-muted);
61
+ }
62
+
63
+ /* knob — gradient face, three grip lines; slides via inset-inline-start (RTL) */
64
+ .juno-switch__track::before {
65
+ content: '';
66
+ position: absolute;
67
+ inset-block-start: var(--juno-space-2);
68
+ inset-inline-start: var(--juno-space-2);
69
+ inline-size: 26px;
70
+ block-size: 22px;
71
+ border-radius: var(--juno-radius-4);
72
+ border: var(--juno-border-width-1) solid var(--juno-knob-edge);
73
+ background:
74
+ repeating-linear-gradient(90deg, var(--juno-knob-grip) 0 2px, transparent 2px 5px) center / 12px
75
+ 14px no-repeat,
76
+ linear-gradient(180deg, var(--juno-knob-face-hi), var(--juno-knob-face-lo));
77
+ box-shadow: 0 2px 4px rgb(0 0 0 / 0.5);
78
+ transition: inset-inline-start var(--juno-motion-duration-quick) var(--juno-motion-ease-standard);
79
+ }
80
+
81
+ /* ON fills the track with a role tint — the filled/empty contrast reads at a
82
+ squint in both modes, where a border-color flip alone does not. */
83
+ .juno-switch__input:checked + .juno-switch__track {
84
+ border-color: var(--juno-role);
85
+ background: color-mix(in srgb, var(--juno-role) 28%, var(--juno-control-surface));
86
+ }
87
+
88
+ .juno-switch__input:checked + .juno-switch__track::after {
89
+ content: 'ON';
90
+ inset-inline: var(--juno-space-8) auto;
91
+ color: var(--juno-data);
92
+ }
93
+
94
+ .juno-switch__input:checked + .juno-switch__track::before {
95
+ inset-inline-start: calc(70px - 26px - var(--juno-space-2));
96
+ }
97
+
98
+ /* Small — quiet per-row toggle (plugin lists, table rows). Drops the printed
99
+ ON/OFF legend; keeps grip, role tint and inset shadow so it reads junoui. */
100
+ .juno-switch--sm .juno-switch__track {
101
+ inline-size: var(--juno-space-40);
102
+ block-size: var(--juno-space-20);
103
+ }
104
+
105
+ .juno-switch--sm .juno-switch__track::after,
106
+ .juno-switch--sm .juno-switch__input:checked + .juno-switch__track::after {
107
+ content: none;
108
+ }
109
+
110
+ .juno-switch--sm .juno-switch__track::before {
111
+ inline-size: var(--juno-space-16);
112
+ block-size: var(--juno-space-12);
113
+ background:
114
+ repeating-linear-gradient(90deg, var(--juno-knob-grip) 0 2px, transparent 2px 5px) center / 8px
115
+ 8px no-repeat,
116
+ linear-gradient(180deg, var(--juno-knob-face-hi), var(--juno-knob-face-lo));
117
+ }
118
+
119
+ .juno-switch--sm .juno-switch__input:checked + .juno-switch__track::before {
120
+ inset-inline-start: calc(var(--juno-space-40) - var(--juno-space-16) - var(--juno-space-2));
121
+ }
122
+
123
+ .juno-switch__input:focus-visible + .juno-switch__track {
124
+ outline: var(--juno-border-width-2) solid var(--juno-active);
125
+ outline-offset: var(--juno-space-2);
126
+ }
127
+
128
+ .juno-switch__input:disabled + .juno-switch__track {
129
+ opacity: var(--juno-opacity-disabled);
130
+ cursor: not-allowed;
131
+ }