@jelinek/ui 0.3.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/README.md +492 -0
- package/dist/components/Alert.svelte +59 -0
- package/dist/components/Alert.svelte.d.ts +13 -0
- package/dist/components/Button.svelte +178 -0
- package/dist/components/Button.svelte.d.ts +19 -0
- package/dist/components/Card.svelte +68 -0
- package/dist/components/Card.svelte.d.ts +11 -0
- package/dist/components/ChapterNav.svelte +290 -0
- package/dist/components/ChapterNav.svelte.d.ts +18 -0
- package/dist/components/Checkbox.svelte +52 -0
- package/dist/components/Checkbox.svelte.d.ts +11 -0
- package/dist/components/CodeCopy.svelte +294 -0
- package/dist/components/CodeCopy.svelte.d.ts +51 -0
- package/dist/components/Container.svelte +34 -0
- package/dist/components/Container.svelte.d.ts +10 -0
- package/dist/components/ContentLogo.svelte +151 -0
- package/dist/components/ContentLogo.svelte.d.ts +37 -0
- package/dist/components/Demo.svelte +111 -0
- package/dist/components/Demo.svelte.d.ts +12 -0
- package/dist/components/Eyebrow.svelte +70 -0
- package/dist/components/Eyebrow.svelte.d.ts +12 -0
- package/dist/components/Glass.svelte +75 -0
- package/dist/components/Glass.svelte.d.ts +10 -0
- package/dist/components/Grid.svelte +87 -0
- package/dist/components/Grid.svelte.d.ts +11 -0
- package/dist/components/Label.svelte +37 -0
- package/dist/components/Label.svelte.d.ts +11 -0
- package/dist/components/PageHero.svelte +312 -0
- package/dist/components/PageHero.svelte.d.ts +55 -0
- package/dist/components/Pager.svelte +98 -0
- package/dist/components/Pager.svelte.d.ts +13 -0
- package/dist/components/Panel.svelte +89 -0
- package/dist/components/Panel.svelte.d.ts +12 -0
- package/dist/components/ProductCard.svelte +81 -0
- package/dist/components/ProductCard.svelte.d.ts +18 -0
- package/dist/components/Radio.svelte +54 -0
- package/dist/components/Radio.svelte.d.ts +11 -0
- package/dist/components/RadioGroup.svelte +42 -0
- package/dist/components/RadioGroup.svelte.d.ts +12 -0
- package/dist/components/Select.svelte +65 -0
- package/dist/components/Select.svelte.d.ts +20 -0
- package/dist/components/SiteFooter.svelte +121 -0
- package/dist/components/SiteFooter.svelte.d.ts +13 -0
- package/dist/components/SiteHeader.svelte +613 -0
- package/dist/components/SiteHeader.svelte.d.ts +80 -0
- package/dist/components/Slider.svelte +55 -0
- package/dist/components/Slider.svelte.d.ts +14 -0
- package/dist/components/Swatch.svelte +174 -0
- package/dist/components/Swatch.svelte.d.ts +58 -0
- package/dist/components/Tab.svelte +88 -0
- package/dist/components/Tab.svelte.d.ts +11 -0
- package/dist/components/Tabs.svelte +306 -0
- package/dist/components/Tabs.svelte.d.ts +20 -0
- package/dist/components/Tag.svelte +71 -0
- package/dist/components/Tag.svelte.d.ts +16 -0
- package/dist/components/TextField.svelte +100 -0
- package/dist/components/TextField.svelte.d.ts +18 -0
- package/dist/components/ThemeToggle.svelte +122 -0
- package/dist/components/ThemeToggle.svelte.d.ts +11 -0
- package/dist/components/Tile.svelte +115 -0
- package/dist/components/Tile.svelte.d.ts +14 -0
- package/dist/components/TileScroller.svelte +199 -0
- package/dist/components/TileScroller.svelte.d.ts +25 -0
- package/dist/components/container-context.d.ts +1 -0
- package/dist/components/container-context.js +6 -0
- package/dist/components/deer-mark-path.d.ts +16 -0
- package/dist/components/deer-mark-path.js +16 -0
- package/dist/components/nav.d.ts +25 -0
- package/dist/components/nav.js +17 -0
- package/dist/components/radio-context.d.ts +6 -0
- package/dist/components/radio-context.js +1 -0
- package/dist/components/tabs-context.d.ts +5 -0
- package/dist/components/tabs-context.js +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +37 -0
- package/dist/theme/base.css +439 -0
- package/dist/theme/extras.css +2 -0
- package/dist/theme/fonts.css +107 -0
- package/dist/theme/tokens.css +208 -0
- package/dist/utils/chapter-nav-dock.svelte.d.ts +23 -0
- package/dist/utils/chapter-nav-dock.svelte.js +65 -0
- package/dist/utils/cn.d.ts +2 -0
- package/dist/utils/cn.js +27 -0
- package/package.json +79 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { type NavItem } from './nav.js';
|
|
4
|
+
interface Props {
|
|
5
|
+
/**
|
|
6
|
+
* Escape hatch: a snippet rendering custom mark content in place of the
|
|
7
|
+
* bundled, re-theming deer mark (e.g. a division-specific logo). Most
|
|
8
|
+
* consumers should leave this unset and get the guide's own mark.
|
|
9
|
+
*/
|
|
10
|
+
logo?: Snippet;
|
|
11
|
+
logoAlt?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Where the logo/mark links to. Defaults to `/`, the guide's own
|
|
14
|
+
* literal href (partials/header.html:3) — set this when the consuming
|
|
15
|
+
* app is not rooted at `/`, e.g. mounted under a base path.
|
|
16
|
+
*/
|
|
17
|
+
logoHref?: string;
|
|
18
|
+
subtitle?: string;
|
|
19
|
+
items?: NavItem[];
|
|
20
|
+
active?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Set when a `ChapterNav` is rendered directly below this header, to
|
|
23
|
+
* reproduce the guide's joined-panel seam (jelinek.css:604-620): the
|
|
24
|
+
* header loses its bottom border/corners/shadow so it flows into the
|
|
25
|
+
* nav strip, until the reader scrolls past 50px and it detaches into
|
|
26
|
+
* its own rounded, shadowed bar (assets/js/jelinek.js:354). SiteHeader
|
|
27
|
+
* tracks that scroll threshold itself — a consumer never has to wire
|
|
28
|
+
* up a listener. Leave unset (default `false`) for the normal,
|
|
29
|
+
* always-detached header, e.g. the rozcestník, which has no chapter
|
|
30
|
+
* nav at all.
|
|
31
|
+
*/
|
|
32
|
+
hasChapterNav?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Accessible name for the `<nav>` landmark holding the top-level
|
|
35
|
+
* links. Czech by default, like every other user-facing string this
|
|
36
|
+
* kit ships (see Pager's `prevLabel`/`nextLabel`).
|
|
37
|
+
*/
|
|
38
|
+
navLabel?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Chapter links to repeat inside the phone menu panel. Below 640px the
|
|
41
|
+
* guide's chapter bar is `display: none` (see ChapterNav.svelte), so the
|
|
42
|
+
* chapters have nowhere else to live on a phone; the guide's own script
|
|
43
|
+
* clones them out of the rendered bar, and this prop is that same content
|
|
44
|
+
* passed declaratively instead. Leave unset on pages with no chapter nav
|
|
45
|
+
* (the rozcestník) — the divider and the block disappear with it. They
|
|
46
|
+
* are hidden again from 640px up, where the bar itself is back.
|
|
47
|
+
*/
|
|
48
|
+
chapterItems?: NavItem[];
|
|
49
|
+
/**
|
|
50
|
+
* Accessible name for the phone hamburger button while the menu is SHUT.
|
|
51
|
+
* It has no visible text — it is three bars that fold into a cross — so
|
|
52
|
+
* this is the only name a screen reader gets. See BURGER below.
|
|
53
|
+
*/
|
|
54
|
+
menuLabel?: string;
|
|
55
|
+
/** Accessible name for the same button while the menu is OPEN. */
|
|
56
|
+
menuLabelClose?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Task 37 — TRAILING CONTENT INSIDE THE NAV, which the guide requires and
|
|
59
|
+
* this component previously refused.
|
|
60
|
+
*
|
|
61
|
+
* partials/header.html:6-13 nests the theme toggle as the LAST CHILD of
|
|
62
|
+
* `.site-header__nav`, so it sits in the nav row on a desktop and moves
|
|
63
|
+
* into the burger panel on a phone along with the links. The earlier
|
|
64
|
+
* decision here was to keep ThemeToggle separate and let a consumer
|
|
65
|
+
* "render both together in its own layout" — but a consumer cannot put
|
|
66
|
+
* anything inside this component's nav from outside it, so what that
|
|
67
|
+
* actually produced was a toggle stranded in the page body, at a different
|
|
68
|
+
* place from the guide and gone from the phone menu entirely.
|
|
69
|
+
*
|
|
70
|
+
* Rendered into BOTH navs, after the links, exactly like `items`. Keep it
|
|
71
|
+
* to controls that belong to the header (the toggle, a division switch) —
|
|
72
|
+
* it is not a general slot.
|
|
73
|
+
*/
|
|
74
|
+
actions?: Snippet;
|
|
75
|
+
class?: string;
|
|
76
|
+
}
|
|
77
|
+
type $$ComponentProps = Props & HTMLAttributes<HTMLElement>;
|
|
78
|
+
declare const SiteHeader: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
79
|
+
type SiteHeader = ReturnType<typeof SiteHeader>;
|
|
80
|
+
export default SiteHeader;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
3
|
+
import { cn } from '../utils/cn.js';
|
|
4
|
+
import Label from './Label.svelte';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
value?: number;
|
|
8
|
+
min?: number;
|
|
9
|
+
max?: number;
|
|
10
|
+
step?: number;
|
|
11
|
+
label?: string;
|
|
12
|
+
id?: string;
|
|
13
|
+
class?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
value = $bindable(0),
|
|
18
|
+
min = 0,
|
|
19
|
+
max = 100,
|
|
20
|
+
step = 1,
|
|
21
|
+
label,
|
|
22
|
+
id,
|
|
23
|
+
class: className,
|
|
24
|
+
...rest
|
|
25
|
+
}: Props & Omit<HTMLInputAttributes, 'value' | 'min' | 'max' | 'step' | 'type'> = $props();
|
|
26
|
+
|
|
27
|
+
const uid = $props.id();
|
|
28
|
+
const inputId = $derived(id ?? uid);
|
|
29
|
+
|
|
30
|
+
// Same reasoning as Checkbox.svelte/Radio.svelte: jelinek.css has NO rule
|
|
31
|
+
// at all for range inputs. ui/komponenty.html's two sliders do carry an
|
|
32
|
+
// inline `accent-color: var(--JELINEK-MAGENTA)`, but both are
|
|
33
|
+
// mattress-width demos (90 cm, 120 cm) — a mattress-CONTEXT instance, not
|
|
34
|
+
// a stylesheet rule for sliders in general. Treating that literal magenta
|
|
35
|
+
// as the default would leak the mattress accent onto furniture-division
|
|
36
|
+
// pages, mixing brown and magenta in one interface exactly as the guide
|
|
37
|
+
// prohibits. Using accent-jelinek-brown instead still reproduces the
|
|
38
|
+
// guide's magenta slider exactly on mattress-division pages, because
|
|
39
|
+
// tokens.css:161-166 re-themes --JELINEK-BROWN to --JELINEK-MAGENTA
|
|
40
|
+
// there — the same division re-theme Checkbox and Radio rely on.
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
{#if label}
|
|
44
|
+
<Label for={inputId}>{label}</Label>
|
|
45
|
+
{/if}
|
|
46
|
+
<input
|
|
47
|
+
{...rest}
|
|
48
|
+
id={inputId}
|
|
49
|
+
type="range"
|
|
50
|
+
{min}
|
|
51
|
+
{max}
|
|
52
|
+
{step}
|
|
53
|
+
bind:value
|
|
54
|
+
class={cn('w-full accent-jelinek-brown', className)}
|
|
55
|
+
/>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
2
|
+
interface Props {
|
|
3
|
+
value?: number;
|
|
4
|
+
min?: number;
|
|
5
|
+
max?: number;
|
|
6
|
+
step?: number;
|
|
7
|
+
label?: string;
|
|
8
|
+
id?: string;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
type $$ComponentProps = Props & Omit<HTMLInputAttributes, 'value' | 'min' | 'max' | 'step' | 'type'>;
|
|
12
|
+
declare const Slider: import("svelte").Component<$$ComponentProps, {}, "value">;
|
|
13
|
+
type Slider = ReturnType<typeof Slider>;
|
|
14
|
+
export default Slider;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import { cn } from '../utils/cn.js';
|
|
4
|
+
import CodeCopy from './CodeCopy.svelte';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
color: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
/**
|
|
10
|
+
* A single copyable string under the name. Use this when the swatch has
|
|
11
|
+
* one value regardless of mode. For a token that changes with the mode,
|
|
12
|
+
* use `light`/`dark` instead — see below.
|
|
13
|
+
*/
|
|
14
|
+
value?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Task 37 — THE TWO-MODE ROWS, restored from the guide.
|
|
17
|
+
*
|
|
18
|
+
* jelinek.css:1006-1014 (`.swatch__mode` / `.swatch__mode-lbl`) exists for
|
|
19
|
+
* one purpose: printing a token's LIGHT and DARK value on the same swatch,
|
|
20
|
+
* one labelled row each, because a colour token in this system is a pair
|
|
21
|
+
* and showing only the current mode's half tells a reader half the truth.
|
|
22
|
+
* ui-old/barvy.html used it on every one of its swatches; the kit shipped
|
|
23
|
+
* the chip and the token NAME and nothing else, so the chapter could not
|
|
24
|
+
* answer "what colour is this" at all.
|
|
25
|
+
*
|
|
26
|
+
* Pass both to get the two rows. Each value is copyable on click, and what
|
|
27
|
+
* lands on the clipboard is the VALUE — the previous behaviour, copying
|
|
28
|
+
* the token name from `value`, is what made the copy button useless for
|
|
29
|
+
* anyone reaching for a colour rather than for CSS.
|
|
30
|
+
*
|
|
31
|
+
* A row whose value is a bare em dash renders as a normal row: that is how
|
|
32
|
+
* a token defined in only one mode says so (--border-highlight-white is
|
|
33
|
+
* dark-only), and it is deliberately not silently dropped.
|
|
34
|
+
*/
|
|
35
|
+
light?: string;
|
|
36
|
+
dark?: string;
|
|
37
|
+
/** One line under the values — what the token is for. */
|
|
38
|
+
note?: string;
|
|
39
|
+
/** Row labels, Czech by default like every other string this kit ships. */
|
|
40
|
+
lightLabel?: string;
|
|
41
|
+
darkLabel?: string;
|
|
42
|
+
/**
|
|
43
|
+
* 1px BORDER-SUBTLE hairline on the colour chip itself. ui-old/barvy.html
|
|
44
|
+
* sets it inline on the swatches whose chip would otherwise dissolve into
|
|
45
|
+
* the card behind it (white or near-white halves); reproduced as a prop
|
|
46
|
+
* because which chips need it is knowledge about the COLOUR, not about
|
|
47
|
+
* this component.
|
|
48
|
+
*/
|
|
49
|
+
chipBorder?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* The archive's copy-icon button next to each value (jelinek.js:146-160
|
|
52
|
+
* builds it, jelinek.css:1028-1039 styles it). In the guide it exists only
|
|
53
|
+
* on pages with the colour-format switcher — initColorSwitch() bails out
|
|
54
|
+
* without one — so it is opt-in here too; the Barvy chapter turns it on.
|
|
55
|
+
*/
|
|
56
|
+
copyIcon?: boolean;
|
|
57
|
+
class?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let {
|
|
61
|
+
color,
|
|
62
|
+
name,
|
|
63
|
+
value,
|
|
64
|
+
light,
|
|
65
|
+
dark,
|
|
66
|
+
note,
|
|
67
|
+
lightLabel = 'Light mode',
|
|
68
|
+
darkLabel = 'Dark mode',
|
|
69
|
+
chipBorder = false,
|
|
70
|
+
copyIcon = false,
|
|
71
|
+
class: className,
|
|
72
|
+
...rest
|
|
73
|
+
}: Props & HTMLAttributes<HTMLDivElement> = $props();
|
|
74
|
+
|
|
75
|
+
// jelinek.css:1008-1012 (`.swatch__mode-lbl`): the secondary family at weight
|
|
76
|
+
// 500, 0.68rem, 0.05em tracking, muted2, and a 62px minimum so both labels
|
|
77
|
+
// line their values up in a column instead of ragging.
|
|
78
|
+
const MODE_LABEL =
|
|
79
|
+
'font-secondary text-[0.68rem] font-medium tracking-[0.05em] text-fg-muted2 ' +
|
|
80
|
+
'min-w-[62px] flex-none whitespace-nowrap';
|
|
81
|
+
// jelinek.css:1007 (`.swatch__mode`): the label and the value on one line.
|
|
82
|
+
const MODE_ROW = 'flex items-center gap-2.5';
|
|
83
|
+
|
|
84
|
+
// CSS FIDELITY
|
|
85
|
+
// jelinek.css:474-482 (shared box system with .card/.panel/.demo/.tile):
|
|
86
|
+
// --box-accent is never overridden for .swatch (no `.swatch--*` colour
|
|
87
|
+
// modifier exists anywhere in jelinek.css), so — same reasoning as
|
|
88
|
+
// Card/Tile — the two-stop border gradient collapses to a flat 1px
|
|
89
|
+
// border-border-subtle, reproduced directly.
|
|
90
|
+
// jelinek.css:665 `.swatch { overflow: hidden }` — clips the color chip's
|
|
91
|
+
// square corners to the card's rounded ones.
|
|
92
|
+
// jelinek.css:666 `.swatch__color { height: 92px }` — the brief's `h-20`
|
|
93
|
+
// (80px) was wrong; 92px isn't on Tailwind's default scale, kept as
|
|
94
|
+
// h-[92px].
|
|
95
|
+
// jelinek.css:667 `.swatch__meta { padding: 12px 14px; font-size: 0.82rem
|
|
96
|
+
// }` — 12px/14px map exactly to Tailwind's py-3/px-3.5; 0.82rem isn't a
|
|
97
|
+
// --step token (no rung of the scale equals it: --step-down2 is 0.75rem
|
|
98
|
+
// and --step-down1 is 0.875rem) so it's an arbitrary text-[0.82rem].
|
|
99
|
+
// jelinek.css:668 `.swatch__meta strong { display: block; font-size:
|
|
100
|
+
// 0.88rem }` — block text-[0.88rem] on the name element directly (this
|
|
101
|
+
// component owns its own markup here, unlike Card/Tile's opaque Snippet
|
|
102
|
+
// children, so no [&_strong] selector trick is needed).
|
|
103
|
+
// jelinek.css:327-331 (shared strong/b→font-medium rule) also covers this
|
|
104
|
+
// strong (`.swatch strong` is in that selector list) — folded into the
|
|
105
|
+
// same class list as font-medium.
|
|
106
|
+
// jelinek.css:322-325 (`.swatch` on the shared list): font-secondary
|
|
107
|
+
// font-light for the swatch's own text.
|
|
108
|
+
// jelinek.css:669-676 `.swatch__meta code` — this is CodeCopy's job; see
|
|
109
|
+
// CodeCopy.svelte for the full fidelity note including the `.is-empty`
|
|
110
|
+
// state. Composing CodeCopy here (rather than a bare <code>) is what
|
|
111
|
+
// reproduces jelinek.css's real click-to-copy behaviour for the value
|
|
112
|
+
// (assets/js/jelinek.js:142-193 wires exactly this element to
|
|
113
|
+
// navigator.clipboard).
|
|
114
|
+
// Standing rule 5 (margin policy): `.panel, .card, .demo { ... margin-
|
|
115
|
+
// bottom: 1.5rem }` (jelinek.css:483) does NOT list .swatch at all — .swatch
|
|
116
|
+
// never had an outer margin to begin with, so there is nothing to drop
|
|
117
|
+
// here (unlike Card/Alert/Panel/Demo).
|
|
118
|
+
// TASK 34 — the dark-mode inset top highlight, jelinek.css:251-257:
|
|
119
|
+
// `:root[data-theme="dark"] .panel, .card, .demo, .swatch, .logo-tool {
|
|
120
|
+
// box-shadow: inset 0 2px 1px -1px var(--box-primary) }`. `.swatch` is in
|
|
121
|
+
// that selector list and this was missing. See Panel.svelte for the full
|
|
122
|
+
// note on the false "nothing consumes --box-primary" reasoning it was
|
|
123
|
+
// dropped under.
|
|
124
|
+
//
|
|
125
|
+
// Mostly INVISIBLE here, in the guide exactly as much as in the kit: an
|
|
126
|
+
// inset box-shadow paints above the element's own background but below its
|
|
127
|
+
// descendants, and `.swatch__color` (the 92px chip, reproduced below) covers
|
|
128
|
+
// the whole top edge the highlight sits on. It shows only on a swatch with
|
|
129
|
+
// no chip. Reproduced anyway — the divergence in the computed value is what
|
|
130
|
+
// the css-lock's green is promising, and "you cannot see it on this
|
|
131
|
+
// component" is not a reason for the two stylesheets to disagree.
|
|
132
|
+
// No `.swatch--*` colour modifier exists, so --box-primary is always the
|
|
133
|
+
// :root 50% grey here.
|
|
134
|
+
const BASE =
|
|
135
|
+
'overflow-hidden rounded-card border border-border-subtle bg-surface ' +
|
|
136
|
+
'dark:shadow-[inset_0_2px_1px_-1px_var(--box-primary)]';
|
|
137
|
+
</script>
|
|
138
|
+
|
|
139
|
+
<div {...rest} class={cn(BASE, className)}>
|
|
140
|
+
<div
|
|
141
|
+
data-swatch-chip
|
|
142
|
+
style="background: {color};"
|
|
143
|
+
class="h-[92px] w-full {chipBorder ? 'border border-border-subtle' : ''}"
|
|
144
|
+
></div>
|
|
145
|
+
{#if name || value || light || dark || note}
|
|
146
|
+
<div class="px-3.5 py-3 font-secondary text-[0.82rem] font-light">
|
|
147
|
+
{#if name}
|
|
148
|
+
<strong class="block text-[0.88rem] font-medium">{name}</strong>
|
|
149
|
+
{/if}
|
|
150
|
+
{#if value}
|
|
151
|
+
<CodeCopy {value} icon={copyIcon} />
|
|
152
|
+
{/if}
|
|
153
|
+
<!-- jelinek.css:1025 (`.swatch__mode .swatch__value`): inside a mode row
|
|
154
|
+
the value row flexes to fill and carries `margin: 3px 0` instead of
|
|
155
|
+
the standalone row's `2px 0 4px`. Only meaningful with the icon —
|
|
156
|
+
a bare CodeCopy is a plain inline button, not a row. -->
|
|
157
|
+
{#if light}
|
|
158
|
+
<div class="{MODE_ROW} mt-1.5">
|
|
159
|
+
<span class={MODE_LABEL}>{lightLabel}</span>
|
|
160
|
+
<CodeCopy value={light} icon={copyIcon} class={copyIcon ? 'flex-1 my-[3px]' : undefined} />
|
|
161
|
+
</div>
|
|
162
|
+
{/if}
|
|
163
|
+
{#if dark}
|
|
164
|
+
<div class={MODE_ROW}>
|
|
165
|
+
<span class={MODE_LABEL}>{darkLabel}</span>
|
|
166
|
+
<CodeCopy value={dark} icon={copyIcon} class={copyIcon ? 'flex-1 my-[3px]' : undefined} />
|
|
167
|
+
</div>
|
|
168
|
+
{/if}
|
|
169
|
+
{#if note}
|
|
170
|
+
<p class="mt-1.5 mb-0 text-fg-muted1">{note}</p>
|
|
171
|
+
{/if}
|
|
172
|
+
</div>
|
|
173
|
+
{/if}
|
|
174
|
+
</div>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
|
+
interface Props {
|
|
3
|
+
color: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
/**
|
|
6
|
+
* A single copyable string under the name. Use this when the swatch has
|
|
7
|
+
* one value regardless of mode. For a token that changes with the mode,
|
|
8
|
+
* use `light`/`dark` instead — see below.
|
|
9
|
+
*/
|
|
10
|
+
value?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Task 37 — THE TWO-MODE ROWS, restored from the guide.
|
|
13
|
+
*
|
|
14
|
+
* jelinek.css:1006-1014 (`.swatch__mode` / `.swatch__mode-lbl`) exists for
|
|
15
|
+
* one purpose: printing a token's LIGHT and DARK value on the same swatch,
|
|
16
|
+
* one labelled row each, because a colour token in this system is a pair
|
|
17
|
+
* and showing only the current mode's half tells a reader half the truth.
|
|
18
|
+
* ui-old/barvy.html used it on every one of its swatches; the kit shipped
|
|
19
|
+
* the chip and the token NAME and nothing else, so the chapter could not
|
|
20
|
+
* answer "what colour is this" at all.
|
|
21
|
+
*
|
|
22
|
+
* Pass both to get the two rows. Each value is copyable on click, and what
|
|
23
|
+
* lands on the clipboard is the VALUE — the previous behaviour, copying
|
|
24
|
+
* the token name from `value`, is what made the copy button useless for
|
|
25
|
+
* anyone reaching for a colour rather than for CSS.
|
|
26
|
+
*
|
|
27
|
+
* A row whose value is a bare em dash renders as a normal row: that is how
|
|
28
|
+
* a token defined in only one mode says so (--border-highlight-white is
|
|
29
|
+
* dark-only), and it is deliberately not silently dropped.
|
|
30
|
+
*/
|
|
31
|
+
light?: string;
|
|
32
|
+
dark?: string;
|
|
33
|
+
/** One line under the values — what the token is for. */
|
|
34
|
+
note?: string;
|
|
35
|
+
/** Row labels, Czech by default like every other string this kit ships. */
|
|
36
|
+
lightLabel?: string;
|
|
37
|
+
darkLabel?: string;
|
|
38
|
+
/**
|
|
39
|
+
* 1px BORDER-SUBTLE hairline on the colour chip itself. ui-old/barvy.html
|
|
40
|
+
* sets it inline on the swatches whose chip would otherwise dissolve into
|
|
41
|
+
* the card behind it (white or near-white halves); reproduced as a prop
|
|
42
|
+
* because which chips need it is knowledge about the COLOUR, not about
|
|
43
|
+
* this component.
|
|
44
|
+
*/
|
|
45
|
+
chipBorder?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* The archive's copy-icon button next to each value (jelinek.js:146-160
|
|
48
|
+
* builds it, jelinek.css:1028-1039 styles it). In the guide it exists only
|
|
49
|
+
* on pages with the colour-format switcher — initColorSwitch() bails out
|
|
50
|
+
* without one — so it is opt-in here too; the Barvy chapter turns it on.
|
|
51
|
+
*/
|
|
52
|
+
copyIcon?: boolean;
|
|
53
|
+
class?: string;
|
|
54
|
+
}
|
|
55
|
+
type $$ComponentProps = Props & HTMLAttributes<HTMLDivElement>;
|
|
56
|
+
declare const Swatch: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
57
|
+
type Swatch = ReturnType<typeof Swatch>;
|
|
58
|
+
export default Swatch;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getContext, type Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
4
|
+
import { cn } from '../utils/cn.js';
|
|
5
|
+
import { TABS_CONTEXT, type TabsContext } from './tabs-context.js';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
value: string;
|
|
9
|
+
class?: string;
|
|
10
|
+
children: Snippet;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let {
|
|
14
|
+
value,
|
|
15
|
+
class: className,
|
|
16
|
+
children,
|
|
17
|
+
...rest
|
|
18
|
+
}: Props & HTMLButtonAttributes = $props();
|
|
19
|
+
|
|
20
|
+
const tabs = getContext<TabsContext>(TABS_CONTEXT);
|
|
21
|
+
if (!tabs) throw new Error('<Tab> must be rendered inside <Tabs>.');
|
|
22
|
+
|
|
23
|
+
const selected = $derived(tabs.current() === value);
|
|
24
|
+
|
|
25
|
+
// jelinek.css:877-884 (.tab, shared with .logo-tool__bgtab/.logo-tool__lang):
|
|
26
|
+
// text-align:center + white-space:nowrap — both absent from the original
|
|
27
|
+
// draft. font-family is
|
|
28
|
+
// --font-secondary (Avenir), NOT --font-primary (GT Walsheim) as first
|
|
29
|
+
// written. font-weight is 700 (font-bold), also missing. Padding is
|
|
30
|
+
// 10px/16px: px-4 (16px) is correct but the vertical 10px is py-2.5, not
|
|
31
|
+
// py-2 (8px). font-size is a fixed 0.9rem literal, not on the --step scale
|
|
32
|
+
// (--step0 is a constant 1rem, unrelated) — kept as text-[0.9rem], with
|
|
33
|
+
// leading-[1.2] for the CSS's line-height:1.2 (same arbitrary-leading
|
|
34
|
+
// pattern as Button's text-[0.98rem] leading-[1.2]).
|
|
35
|
+
//
|
|
36
|
+
// TASK 31 — `flex: 1` BECAME `flex: 1 0 auto` (jelinek.css:880), which is
|
|
37
|
+
// `flex-[1_0_auto]` here and NOT Tailwind's `flex-1` (that is `flex: 1 1 0%`,
|
|
38
|
+
// a different thing: grow, shrink, zero basis). The guide's own inline
|
|
39
|
+
// comment on that line says what it is for — "roztáhne se, ale nikdy se
|
|
40
|
+
// nesmrskne pod text", it stretches but never shrinks below its label.
|
|
41
|
+
//
|
|
42
|
+
// This is the other half of Tabs' change from a wrapping row to a sideways
|
|
43
|
+
// scroller; see Tabs.svelte's comment for the full pairing. Short version:
|
|
44
|
+
// with a 0% basis, segments in a `flex-nowrap` row shrink instead of
|
|
45
|
+
// overflowing, so the scroller never engages and long labels squash. Both
|
|
46
|
+
// values have to move together or neither does anything.
|
|
47
|
+
//
|
|
48
|
+
// `transition-all` is NOT from the guide — `.tab` carries no transition at
|
|
49
|
+
// all there, so hover snaps rather than fades. Left in place because it
|
|
50
|
+
// predates this task and removing it is a visual change with no rule behind
|
|
51
|
+
// it either way; noted in the Task 31 report so the call is visible rather
|
|
52
|
+
// than buried.
|
|
53
|
+
const BASE =
|
|
54
|
+
'flex-[1_0_auto] cursor-pointer whitespace-nowrap text-center rounded-pill border-0 ' +
|
|
55
|
+
'px-4 py-2.5 font-secondary text-[0.9rem] leading-[1.2] font-bold transition-all';
|
|
56
|
+
|
|
57
|
+
// jelinek.css:885-887 (.tab:hover): background flips to --text-muted1 with
|
|
58
|
+
// --text-white text on top — not just a text-colour change as first
|
|
59
|
+
// written. jelinek.css:889-891 (.tab.is-active): unchanged, bg-jelinek-black
|
|
60
|
+
// + text-on-jelinek-black is correct. .tab.is-active is declared after
|
|
61
|
+
// .tab:hover in the source with equal specificity, so an active tab keeps
|
|
62
|
+
// its solid styling even while hovered — reproduced here by only adding the
|
|
63
|
+
// hover: classes to the unselected branch, never to the selected one.
|
|
64
|
+
// Neither branch needs an explicit dark: override: bg-jelinek-black /
|
|
65
|
+
// text-on-jelinek-black / text-fg-muted1 / bg-fg-muted1 / text-fg-inverse
|
|
66
|
+
// all resolve through theme-aware CSS custom properties that already flip
|
|
67
|
+
// under [data-theme="dark"]/.dark.
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<button
|
|
71
|
+
{...rest}
|
|
72
|
+
type={rest.type ?? 'button'}
|
|
73
|
+
role={rest.role ?? 'tab'}
|
|
74
|
+
aria-selected={selected}
|
|
75
|
+
onclick={(event) => {
|
|
76
|
+
tabs.select(value);
|
|
77
|
+
rest.onclick?.(event);
|
|
78
|
+
}}
|
|
79
|
+
class={cn(
|
|
80
|
+
BASE,
|
|
81
|
+
selected
|
|
82
|
+
? 'bg-jelinek-black text-on-jelinek-black'
|
|
83
|
+
: 'bg-transparent text-fg-muted1 hover:bg-fg-muted1 hover:text-fg-inverse',
|
|
84
|
+
className
|
|
85
|
+
)}
|
|
86
|
+
>
|
|
87
|
+
{@render children()}
|
|
88
|
+
</button>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
3
|
+
interface Props {
|
|
4
|
+
value: string;
|
|
5
|
+
class?: string;
|
|
6
|
+
children: Snippet;
|
|
7
|
+
}
|
|
8
|
+
type $$ComponentProps = Props & HTMLButtonAttributes;
|
|
9
|
+
declare const Tab: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
10
|
+
type Tab = ReturnType<typeof Tab>;
|
|
11
|
+
export default Tab;
|