@jelinek/ui 0.9.0 → 0.9.1

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 (85) hide show
  1. package/dist/components/ChapterNav.svelte +1 -1
  2. package/dist/components/Checkbox.svelte +13 -15
  3. package/dist/components/Checkbox.svelte.d.ts +1 -0
  4. package/dist/components/ContentLogo.svelte +18 -13
  5. package/dist/components/ContentLogo.svelte.d.ts +6 -5
  6. package/dist/components/Grid.svelte +9 -5
  7. package/dist/components/Label.svelte +12 -1
  8. package/dist/components/Label.svelte.d.ts +8 -0
  9. package/dist/components/PageHero.svelte +6 -7
  10. package/dist/components/Pager.svelte +5 -2
  11. package/dist/components/PhotoTile.svelte +4 -2
  12. package/dist/components/ProductCard.svelte +66 -44
  13. package/dist/components/ProductCard.svelte.d.ts +17 -4
  14. package/dist/components/Radio.svelte +12 -8
  15. package/dist/components/Radio.svelte.d.ts +1 -0
  16. package/dist/components/Select.svelte +70 -12
  17. package/dist/components/Select.svelte.d.ts +11 -0
  18. package/dist/components/SiteFooter.svelte +19 -0
  19. package/dist/components/SiteFooter.svelte.d.ts +8 -0
  20. package/dist/components/SiteHeader.svelte +11 -9
  21. package/dist/components/Slider.svelte +6 -11
  22. package/dist/components/Switch.svelte +76 -0
  23. package/dist/components/Switch.svelte.d.ts +12 -0
  24. package/dist/components/TextField.svelte +61 -10
  25. package/dist/components/TextField.svelte.d.ts +13 -0
  26. package/dist/components/Textarea.svelte +92 -0
  27. package/dist/components/Textarea.svelte.d.ts +23 -0
  28. package/dist/components/Tile.svelte +12 -11
  29. package/dist/components/Tile.svelte.d.ts +8 -2
  30. package/dist/components/blocks/CmsButton.svelte +36 -0
  31. package/dist/components/blocks/CmsButton.svelte.d.ts +12 -0
  32. package/dist/components/blocks/CmsColumns.svelte +20 -0
  33. package/dist/components/blocks/CmsColumns.svelte.d.ts +9 -0
  34. package/dist/components/blocks/CmsCover.svelte +71 -0
  35. package/dist/components/blocks/CmsCover.svelte.d.ts +17 -0
  36. package/dist/components/blocks/CmsHeading.svelte +50 -0
  37. package/dist/components/blocks/CmsHeading.svelte.d.ts +14 -0
  38. package/dist/components/blocks/CmsImage.svelte +43 -0
  39. package/dist/components/blocks/CmsImage.svelte.d.ts +13 -0
  40. package/dist/components/blocks/CmsMediaText.svelte +64 -0
  41. package/dist/components/blocks/CmsMediaText.svelte.d.ts +16 -0
  42. package/dist/components/blocks/CmsText.svelte +53 -0
  43. package/dist/components/blocks/CmsText.svelte.d.ts +10 -0
  44. package/dist/components/commerce/Breadcrumbs.svelte +22 -5
  45. package/dist/components/commerce/CartDrawer.svelte +7 -4
  46. package/dist/components/commerce/CartLineItem.svelte +1 -1
  47. package/dist/components/commerce/CheckoutProgress.svelte +1 -1
  48. package/dist/components/commerce/ConfigSection.svelte +2 -2
  49. package/dist/components/commerce/DimensionSlider.svelte +1 -1
  50. package/dist/components/commerce/Drawer.svelte +38 -24
  51. package/dist/components/commerce/Drawer.svelte.d.ts +0 -2
  52. package/dist/components/commerce/EmptyState.svelte +37 -0
  53. package/dist/components/commerce/EmptyState.svelte.d.ts +13 -0
  54. package/dist/components/commerce/FilterChip.svelte +35 -0
  55. package/dist/components/commerce/FilterChip.svelte.d.ts +10 -0
  56. package/dist/components/commerce/FreeShippingBar.svelte +1 -1
  57. package/dist/components/commerce/Gallery.svelte +4 -2
  58. package/dist/components/commerce/ListingToolbar.svelte +62 -0
  59. package/dist/components/commerce/ListingToolbar.svelte.d.ts +19 -0
  60. package/dist/components/commerce/Modal.svelte +31 -21
  61. package/dist/components/commerce/Modal.svelte.d.ts +0 -2
  62. package/dist/components/commerce/OrderSummary.svelte +1 -1
  63. package/dist/components/commerce/Pagination.svelte +115 -0
  64. package/dist/components/commerce/Pagination.svelte.d.ts +14 -0
  65. package/dist/components/commerce/PriceDisplay.svelte +3 -3
  66. package/dist/components/commerce/ProductHeader.svelte +2 -2
  67. package/dist/components/commerce/QuantityStepper.svelte +1 -1
  68. package/dist/components/commerce/RatingStars.svelte +1 -1
  69. package/dist/components/commerce/SearchField.svelte +92 -0
  70. package/dist/components/commerce/SearchField.svelte.d.ts +15 -0
  71. package/dist/components/commerce/SideToggle.svelte +1 -1
  72. package/dist/components/commerce/Skeleton.svelte +4 -1
  73. package/dist/components/commerce/StickyBar.svelte +4 -4
  74. package/dist/components/commerce/StockBadge.svelte +2 -2
  75. package/dist/components/commerce/Toaster.svelte +9 -5
  76. package/dist/components/commerce/TrustBadges.svelte +1 -1
  77. package/dist/components/commerce/overlay.d.ts +14 -0
  78. package/dist/components/commerce/overlay.js +49 -0
  79. package/dist/index.d.ts +14 -0
  80. package/dist/index.js +14 -0
  81. package/dist/theme/base.css +22 -0
  82. package/dist/theme/extras.css +44 -0
  83. package/dist/theme/fonts.css +6 -42
  84. package/dist/theme/tokens.css +16 -9
  85. package/package.json +1 -1
@@ -0,0 +1,92 @@
1
+ <script lang="ts">
2
+ import type { HTMLInputAttributes } from 'svelte/elements';
3
+ import { cn } from '../../utils/cn.js';
4
+
5
+ interface Props {
6
+ value?: string;
7
+ /** Popisek pro čtečky (pole nemá viditelný label). */
8
+ label?: string;
9
+ placeholder?: string;
10
+ /** Zavolá se při odeslání (Enter) s aktuálním dotazem. */
11
+ onsearch?: (query: string) => void;
12
+ disabled?: boolean;
13
+ class?: string;
14
+ }
15
+
16
+ let {
17
+ value = $bindable(''),
18
+ label = 'Hledat',
19
+ placeholder = 'Hledat…',
20
+ onsearch,
21
+ disabled = false,
22
+ class: className,
23
+ ...rest
24
+ }: Props & Omit<HTMLInputAttributes, 'value'> = $props();
25
+
26
+ // Field chrome is TextField's BASE verbatim (border-fg-muted2, 11px/14px
27
+ // padding, 0.98rem, tokenised focus ring — see TextField.svelte for the
28
+ // full provenance notes), with left padding widened for the magnifier and
29
+ // right padding for the clear button. type="search" is NOT used: WebKit
30
+ // draws its own clear control there, which would double up with ours.
31
+ const FIELD =
32
+ 'w-full rounded-button border border-fg-muted2 bg-surface py-[11px] pr-11 pl-11 ' +
33
+ 'font-secondary font-light text-[0.98rem] text-fg placeholder:text-fg-muted2 ' +
34
+ 'focus:outline-[length:var(--focus-width)] focus:outline-offset-1 ' +
35
+ 'focus:outline-[var(--focus-color)] focus:border-[var(--focus-color)]';
36
+ const DISABLED =
37
+ 'cursor-not-allowed border-border-subtle bg-surface-gray text-fg-muted2 ' +
38
+ 'placeholder:text-fg-muted2';
39
+ </script>
40
+
41
+ <!-- role="search" comes with the <search>/form pattern: submit = Enter, which
42
+ is how people actually search. The magnifier is decorative (the field's
43
+ accessible name carries the meaning), the clear button is a real button. -->
44
+ <form
45
+ role="search"
46
+ class={cn('relative', className)}
47
+ onsubmit={(e) => {
48
+ e.preventDefault();
49
+ onsearch?.(value);
50
+ }}
51
+ >
52
+ <svg
53
+ viewBox="0 0 24 24"
54
+ class="pointer-events-none absolute top-1/2 left-3.5 size-5 -translate-y-1/2 text-fg-muted1"
55
+ fill="none"
56
+ stroke="currentColor"
57
+ stroke-width="2"
58
+ stroke-linecap="round"
59
+ aria-hidden="true"
60
+ >
61
+ <circle cx="11" cy="11" r="7" /><path d="m21 21-4.3-4.3" />
62
+ </svg>
63
+ <input
64
+ {...rest}
65
+ type="text"
66
+ aria-label={label}
67
+ {placeholder}
68
+ {disabled}
69
+ bind:value
70
+ class={cn(FIELD, disabled && DISABLED)}
71
+ />
72
+ {#if value && !disabled}
73
+ <button
74
+ type="button"
75
+ aria-label="Vymazat hledání"
76
+ class="absolute top-1/2 right-2 inline-flex size-8 -translate-y-1/2 cursor-pointer
77
+ items-center justify-center rounded-button text-fg-muted1 transition-colors
78
+ duration-[var(--dur-base)] ease-[var(--ease)] hover:text-fg"
79
+ onclick={() => (value = '')}
80
+ >
81
+ <svg
82
+ viewBox="0 0 24 24"
83
+ class="size-4"
84
+ fill="none"
85
+ stroke="currentColor"
86
+ stroke-width="2"
87
+ stroke-linecap="round"
88
+ aria-hidden="true"><path d="M18 6 6 18M6 6l12 12" /></svg
89
+ >
90
+ </button>
91
+ {/if}
92
+ </form>
@@ -0,0 +1,15 @@
1
+ import type { HTMLInputAttributes } from 'svelte/elements';
2
+ interface Props {
3
+ value?: string;
4
+ /** Popisek pro čtečky (pole nemá viditelný label). */
5
+ label?: string;
6
+ placeholder?: string;
7
+ /** Zavolá se při odeslání (Enter) s aktuálním dotazem. */
8
+ onsearch?: (query: string) => void;
9
+ disabled?: boolean;
10
+ class?: string;
11
+ }
12
+ type $$ComponentProps = Props & Omit<HTMLInputAttributes, 'value'>;
13
+ declare const SearchField: import("svelte").Component<$$ComponentProps, {}, "value">;
14
+ type SearchField = ReturnType<typeof SearchField>;
15
+ export default SearchField;
@@ -27,7 +27,7 @@
27
27
 
28
28
  <button
29
29
  type="button"
30
- class="rounded-card cursor-pointer border px-3 py-2.5 text-left transition-all duration-200 max-sm:px-2 max-sm:py-2 {active
30
+ class="rounded-card cursor-pointer border px-3 py-2.5 text-left font-secondary font-light transition-all duration-200 max-sm:px-2 max-sm:py-2 {active
31
31
  ? 'border-(--JELINEK-BROWN) bg-surface-gray shadow-resting'
32
32
  : 'border-border-subtle bg-surface hover:border-(--text-muted2)'}"
33
33
  aria-pressed={active}
@@ -7,4 +7,7 @@
7
7
  let { class: className = '' }: Props = $props();
8
8
  </script>
9
9
 
10
- <div class="animate-pulse rounded-card bg-surface-gray {className}" aria-hidden="true"></div>
10
+ <!-- Shimmer místo pulzu (majitel, 14. 8. 2026) — projíždějící gradient,
11
+ definovaný v theme/extras.css (.skeleton-shimmer); tam je i klidová
12
+ varianta pro prefers-reduced-motion. -->
13
+ <div class="skeleton-shimmer rounded-card {className}" aria-hidden="true"></div>
@@ -9,8 +9,8 @@
9
9
  let { visible, children }: Props = $props();
10
10
  </script>
11
11
 
12
- <!-- Glass strip under the kit's floating header card. z-40 keeps it BELOW the
13
- header (SiteHeader is z-50) — any higher and the strip renders over the
12
+ <!-- Glass strip under the kit's floating header card. --z-bar (40) keeps it
13
+ BELOW the header (--z-header, 50) — any higher and the strip renders over the
14
14
  card and its backdrop blur smears the whole top of the page. Top offset:
15
15
  var(--header-h) from 640px up so the strip hangs under the fixed card;
16
16
  top-0 on a phone, where the kit bar is absolute and has already scrolled
@@ -18,10 +18,10 @@
18
18
  left the viewport). -->
19
19
  <div
20
20
  aria-hidden={!visible}
21
- class="fixed inset-x-0 top-0 z-40 border-b border-(--glass-border) bg-(--glass-fill) backdrop-blur-[25px] backdrop-saturate-140 transition-transform duration-(--dur-base) min-[640px]:top-(--header-h)
21
+ class="fixed inset-x-0 top-0 z-(--z-bar) border-b border-(--glass-border) bg-(--glass-fill) backdrop-blur-[25px] backdrop-saturate-140 transition-transform duration-(--dur-base) min-[640px]:top-(--header-h)
22
22
  {visible ? 'translate-y-0' : 'pointer-events-none -translate-y-[calc(100%+var(--header-h))]'}"
23
23
  >
24
- <div class="max-w-page mx-auto flex w-full items-center gap-4 px-6 py-3">
24
+ <div class="max-w-page mx-auto flex w-full items-center gap-4 px-6 py-3 font-secondary font-light">
25
25
  {@render children()}
26
26
  </div>
27
27
  </div>
@@ -33,12 +33,12 @@
33
33
  </script>
34
34
 
35
35
  {#if compact}
36
- <span class="inline-flex items-center gap-1.5 text-(length:--step-down1) font-medium {config.cls}">
36
+ <span class="inline-flex items-center gap-1.5 font-secondary text-(length:--step-down1) font-medium {config.cls}">
37
37
  <span class="size-2 rounded-pill {config.dot}"></span>{text}
38
38
  </span>
39
39
  {:else}
40
40
  <span
41
- class="rounded-pill bg-surface-gray inline-flex items-center gap-2 px-3 py-1 text-(length:--step-down1) font-medium {config.cls}"
41
+ class="rounded-pill bg-surface-gray inline-flex items-center gap-2 px-3 py-1 font-secondary text-(length:--step-down1) font-medium {config.cls}"
42
42
  >
43
43
  <span class="size-2 rounded-pill {config.dot}"></span>{text}
44
44
  </span>
@@ -17,16 +17,20 @@
17
17
  onMount(() => toasts.subscribe((next) => (items = next)));
18
18
  </script>
19
19
 
20
- <!-- z-scale contract: header 1100, Drawer/Modal 1200+, search overlays 1400.
21
- Toasts must clear all of it so an add-to-cart confirmation is never hidden
22
- under a drawer/overlay — hence z-[1500]. -->
20
+ <!-- Škála vrstev (jelinek.css :root): hlavička --z-header (50), Drawer/Modal
21
+ --z-overlay (1200), toasty nade vším — --z-toast (1500; potvrzení
22
+ o přidání do košíku nesmí zmizet pod drawerem). role="status"
23
+ + aria-live="polite": kontejner tu je trvale, takže čtečky ohlásí každý
24
+ toast, který do něj přibude — dřív byly hlášky pro čtečky němé (EAA). -->
23
25
  <div
24
- class="pointer-events-none fixed inset-x-0 bottom-6 z-[1500] flex flex-col items-center gap-2 {className}"
26
+ role="status"
27
+ aria-live="polite"
28
+ class="pointer-events-none fixed inset-x-0 bottom-6 z-(--z-toast) flex flex-col items-center gap-2 {className}"
25
29
  >
26
30
  {#each items as t (t.id)}
27
31
  <div
28
32
  transition:fly={{ y: 16, duration: 200 }}
29
- class="rounded-pill text-fg-inverse shadow-interactive pointer-events-auto bg-(--bg-black) px-5 py-2.5 text-(length:--step-down1) font-medium"
33
+ class="rounded-pill text-fg-inverse shadow-interactive pointer-events-auto bg-(--bg-black) px-5 py-2.5 font-secondary text-(length:--step-down1) font-medium"
30
34
  >
31
35
  {t.message}
32
36
  </div>
@@ -17,7 +17,7 @@
17
17
  <div class={overlay ? 'absolute inset-x-3 bottom-3 flex flex-wrap gap-2' : 'flex flex-wrap gap-2'}>
18
18
  {#each items as item (item.label)}
19
19
  <span
20
- class="rounded-pill flex items-center gap-1.5 bg-(--glass-fill) px-3 py-1.5 text-(length:--step-down2) font-medium backdrop-blur-[25px]"
20
+ class="rounded-pill flex items-center gap-1.5 bg-(--glass-fill) px-3 py-1.5 font-secondary text-(length:--step-down2) font-medium backdrop-blur-[25px]"
21
21
  >
22
22
  <item.icon class="size-3.5" />
23
23
  {item.label}
@@ -10,3 +10,17 @@ export declare function portal(node: HTMLElement): {
10
10
  };
11
11
  /** Lock body scroll; returns the unlock function (for use inside $effect). */
12
12
  export declare function lockScroll(): () => void;
13
+ /**
14
+ * Past na fokus (zákonná přístupnost překryvů — EAA): Tab a Shift+Tab
15
+ * cyklí UVNITŘ containeru, do stránky pod overlayem se tabulátorem nedá
16
+ * vyjet. Myš ani Escape to nijak neomezuje. Vrací úklidovou funkci
17
+ * (pro $effect), stejná smlouva jako lockScroll().
18
+ */
19
+ export declare function trapFocus(container: HTMLElement): () => void;
20
+ /**
21
+ * Zapamatuje si prvek zaostřený PŘED otevřením overlaye a vrátí funkci,
22
+ * která na něj fokus vrátí (pokud mezitím nezmizel ze stránky). Čtenář
23
+ * s klávesnicí tak po zavření pokračuje přesně tam, kde byl — typicky
24
+ * na tlačítku, kterým overlay otevřel.
25
+ */
26
+ export declare function rememberFocus(): () => void;
@@ -18,3 +18,52 @@ export function lockScroll() {
18
18
  document.body.style.overflow = prev;
19
19
  };
20
20
  }
21
+ // Co se v překryvu dá zaostřit tabulátorem. Bez filtru viditelnosti —
22
+ // otevřený overlay svůj obsah zobrazuje celý a jsdom (unit testy)
23
+ // offsetParent nezná.
24
+ const FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), ' +
25
+ 'select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
26
+ /**
27
+ * Past na fokus (zákonná přístupnost překryvů — EAA): Tab a Shift+Tab
28
+ * cyklí UVNITŘ containeru, do stránky pod overlayem se tabulátorem nedá
29
+ * vyjet. Myš ani Escape to nijak neomezuje. Vrací úklidovou funkci
30
+ * (pro $effect), stejná smlouva jako lockScroll().
31
+ */
32
+ export function trapFocus(container) {
33
+ const onKey = (e) => {
34
+ if (e.key !== 'Tab')
35
+ return;
36
+ const items = Array.from(container.querySelectorAll(FOCUSABLE));
37
+ if (items.length === 0) {
38
+ e.preventDefault();
39
+ container.focus();
40
+ return;
41
+ }
42
+ const first = items[0];
43
+ const last = items[items.length - 1];
44
+ const active = document.activeElement;
45
+ if (e.shiftKey && (active === first || active === container)) {
46
+ e.preventDefault();
47
+ last.focus();
48
+ }
49
+ else if (!e.shiftKey && active === last) {
50
+ e.preventDefault();
51
+ first.focus();
52
+ }
53
+ };
54
+ container.addEventListener('keydown', onKey);
55
+ return () => container.removeEventListener('keydown', onKey);
56
+ }
57
+ /**
58
+ * Zapamatuje si prvek zaostřený PŘED otevřením overlaye a vrátí funkci,
59
+ * která na něj fokus vrátí (pokud mezitím nezmizel ze stránky). Čtenář
60
+ * s klávesnicí tak po zavření pokračuje přesně tam, kde byl — typicky
61
+ * na tlačítku, kterým overlay otevřel.
62
+ */
63
+ export function rememberFocus() {
64
+ const opener = document.activeElement instanceof HTMLElement ? document.activeElement : null;
65
+ return () => {
66
+ if (opener && document.contains(opener))
67
+ opener.focus();
68
+ };
69
+ }
package/dist/index.d.ts CHANGED
@@ -10,6 +10,13 @@ export { type CartLine } from './components/commerce/cart-types.js';
10
10
  export { default as ChapterNav } from './components/ChapterNav.svelte';
11
11
  export { default as Checkbox } from './components/Checkbox.svelte';
12
12
  export { default as CheckoutProgress } from './components/commerce/CheckoutProgress.svelte';
13
+ export { default as CmsButton } from './components/blocks/CmsButton.svelte';
14
+ export { default as CmsColumns } from './components/blocks/CmsColumns.svelte';
15
+ export { default as CmsCover } from './components/blocks/CmsCover.svelte';
16
+ export { default as CmsHeading } from './components/blocks/CmsHeading.svelte';
17
+ export { default as CmsImage } from './components/blocks/CmsImage.svelte';
18
+ export { default as CmsMediaText } from './components/blocks/CmsMediaText.svelte';
19
+ export { default as CmsText } from './components/blocks/CmsText.svelte';
13
20
  export { default as CodeCopy } from './components/CodeCopy.svelte';
14
21
  export { default as ConfigSection } from './components/commerce/ConfigSection.svelte';
15
22
  export { CONTAINER_CONTEXT } from './components/container-context.js';
@@ -18,19 +25,23 @@ export { default as ContentLogo } from './components/ContentLogo.svelte';
18
25
  export { default as Demo } from './components/Demo.svelte';
19
26
  export { default as DimensionSlider } from './components/commerce/DimensionSlider.svelte';
20
27
  export { default as Drawer } from './components/commerce/Drawer.svelte';
28
+ export { default as EmptyState } from './components/commerce/EmptyState.svelte';
21
29
  export { default as Eyebrow } from './components/Eyebrow.svelte';
30
+ export { default as FilterChip } from './components/commerce/FilterChip.svelte';
22
31
  export { formatMoney, formatPrice } from './components/commerce/money.js';
23
32
  export { default as FreeShippingBar } from './components/commerce/FreeShippingBar.svelte';
24
33
  export { default as Gallery } from './components/commerce/Gallery.svelte';
25
34
  export { default as Glass } from './components/Glass.svelte';
26
35
  export { default as Grid } from './components/Grid.svelte';
27
36
  export { default as Label } from './components/Label.svelte';
37
+ export { default as ListingToolbar } from './components/commerce/ListingToolbar.svelte';
28
38
  export { default as Modal } from './components/commerce/Modal.svelte';
29
39
  export { isActive, type NavItem, type NavGroup } from './components/nav.js';
30
40
  export { default as OptionTile } from './components/OptionTile.svelte';
31
41
  export { default as OrderSummary } from './components/commerce/OrderSummary.svelte';
32
42
  export { default as PageHero } from './components/PageHero.svelte';
33
43
  export { default as Pager } from './components/Pager.svelte';
44
+ export { default as Pagination } from './components/commerce/Pagination.svelte';
34
45
  export { default as Panel } from './components/Panel.svelte';
35
46
  export { default as PhotoTile } from './components/PhotoTile.svelte';
36
47
  export { default as PriceDisplay } from './components/commerce/PriceDisplay.svelte';
@@ -43,6 +54,7 @@ export { default as Radio } from './components/Radio.svelte';
43
54
  export { default as RatingStars } from './components/commerce/RatingStars.svelte';
44
55
  export { RADIO_CONTEXT, type RadioContext } from './components/radio-context.js';
45
56
  export { default as RadioGroup } from './components/RadioGroup.svelte';
57
+ export { default as SearchField } from './components/commerce/SearchField.svelte';
46
58
  export { default as Select } from './components/Select.svelte';
47
59
  export { portal } from './components/commerce/overlay.js';
48
60
  export { default as SideToggle } from './components/commerce/SideToggle.svelte';
@@ -53,10 +65,12 @@ export { default as Slider } from './components/Slider.svelte';
53
65
  export { default as StickyBar } from './components/commerce/StickyBar.svelte';
54
66
  export { default as StockBadge } from './components/commerce/StockBadge.svelte';
55
67
  export { default as Swatch } from './components/Swatch.svelte';
68
+ export { default as Switch } from './components/Switch.svelte';
56
69
  export { default as Tab } from './components/Tab.svelte';
57
70
  export { default as Tabs } from './components/Tabs.svelte';
58
71
  export { TABS_CONTEXT, type TabsContext } from './components/tabs-context.js';
59
72
  export { default as Tag } from './components/Tag.svelte';
73
+ export { default as Textarea } from './components/Textarea.svelte';
60
74
  export { default as TextField } from './components/TextField.svelte';
61
75
  export { default as ThemeToggle } from './components/ThemeToggle.svelte';
62
76
  export { default as Tile } from './components/Tile.svelte';
package/dist/index.js CHANGED
@@ -10,6 +10,13 @@ export {} from './components/commerce/cart-types.js';
10
10
  export { default as ChapterNav } from './components/ChapterNav.svelte';
11
11
  export { default as Checkbox } from './components/Checkbox.svelte';
12
12
  export { default as CheckoutProgress } from './components/commerce/CheckoutProgress.svelte';
13
+ export { default as CmsButton } from './components/blocks/CmsButton.svelte';
14
+ export { default as CmsColumns } from './components/blocks/CmsColumns.svelte';
15
+ export { default as CmsCover } from './components/blocks/CmsCover.svelte';
16
+ export { default as CmsHeading } from './components/blocks/CmsHeading.svelte';
17
+ export { default as CmsImage } from './components/blocks/CmsImage.svelte';
18
+ export { default as CmsMediaText } from './components/blocks/CmsMediaText.svelte';
19
+ export { default as CmsText } from './components/blocks/CmsText.svelte';
13
20
  export { default as CodeCopy } from './components/CodeCopy.svelte';
14
21
  export { default as ConfigSection } from './components/commerce/ConfigSection.svelte';
15
22
  export { CONTAINER_CONTEXT } from './components/container-context.js';
@@ -18,19 +25,23 @@ export { default as ContentLogo } from './components/ContentLogo.svelte';
18
25
  export { default as Demo } from './components/Demo.svelte';
19
26
  export { default as DimensionSlider } from './components/commerce/DimensionSlider.svelte';
20
27
  export { default as Drawer } from './components/commerce/Drawer.svelte';
28
+ export { default as EmptyState } from './components/commerce/EmptyState.svelte';
21
29
  export { default as Eyebrow } from './components/Eyebrow.svelte';
30
+ export { default as FilterChip } from './components/commerce/FilterChip.svelte';
22
31
  export { formatMoney, formatPrice } from './components/commerce/money.js';
23
32
  export { default as FreeShippingBar } from './components/commerce/FreeShippingBar.svelte';
24
33
  export { default as Gallery } from './components/commerce/Gallery.svelte';
25
34
  export { default as Glass } from './components/Glass.svelte';
26
35
  export { default as Grid } from './components/Grid.svelte';
27
36
  export { default as Label } from './components/Label.svelte';
37
+ export { default as ListingToolbar } from './components/commerce/ListingToolbar.svelte';
28
38
  export { default as Modal } from './components/commerce/Modal.svelte';
29
39
  export { isActive } from './components/nav.js';
30
40
  export { default as OptionTile } from './components/OptionTile.svelte';
31
41
  export { default as OrderSummary } from './components/commerce/OrderSummary.svelte';
32
42
  export { default as PageHero } from './components/PageHero.svelte';
33
43
  export { default as Pager } from './components/Pager.svelte';
44
+ export { default as Pagination } from './components/commerce/Pagination.svelte';
34
45
  export { default as Panel } from './components/Panel.svelte';
35
46
  export { default as PhotoTile } from './components/PhotoTile.svelte';
36
47
  export { default as PriceDisplay } from './components/commerce/PriceDisplay.svelte';
@@ -43,6 +54,7 @@ export { default as Radio } from './components/Radio.svelte';
43
54
  export { default as RatingStars } from './components/commerce/RatingStars.svelte';
44
55
  export { RADIO_CONTEXT } from './components/radio-context.js';
45
56
  export { default as RadioGroup } from './components/RadioGroup.svelte';
57
+ export { default as SearchField } from './components/commerce/SearchField.svelte';
46
58
  export { default as Select } from './components/Select.svelte';
47
59
  export { portal } from './components/commerce/overlay.js';
48
60
  export { default as SideToggle } from './components/commerce/SideToggle.svelte';
@@ -53,10 +65,12 @@ export { default as Slider } from './components/Slider.svelte';
53
65
  export { default as StickyBar } from './components/commerce/StickyBar.svelte';
54
66
  export { default as StockBadge } from './components/commerce/StockBadge.svelte';
55
67
  export { default as Swatch } from './components/Swatch.svelte';
68
+ export { default as Switch } from './components/Switch.svelte';
56
69
  export { default as Tab } from './components/Tab.svelte';
57
70
  export { default as Tabs } from './components/Tabs.svelte';
58
71
  export { TABS_CONTEXT } from './components/tabs-context.js';
59
72
  export { default as Tag } from './components/Tag.svelte';
73
+ export { default as Textarea } from './components/Textarea.svelte';
60
74
  export { default as TextField } from './components/TextField.svelte';
61
75
  export { default as ThemeToggle } from './components/ThemeToggle.svelte';
62
76
  export { default as Tile } from './components/Tile.svelte';
@@ -253,6 +253,25 @@
253
253
  color var(--dur-slow) var(--ease);
254
254
  }
255
255
 
256
+ /* KURZÍVA SE NEPOUŽÍVÁ NIKDY (brand → Typografie; majitel 14. 8. 2026) —
257
+ žádné písmo značky nemá skloněný řez a prohlížeč by si ho syntetizoval.
258
+ <em>/<i> se narovnává a zvýrazňuje tučně („ke zvýraznění slouží tučné
259
+ řezy“). Mirrors the identical rule in jelinek.css. */
260
+ em,
261
+ i {
262
+ font-style: normal;
263
+ font-weight: 700;
264
+ }
265
+
266
+ /* <strong> pinned to Bold: the default `bolder` is RELATIVE — one step
267
+ up from Light (300) is Regular (400), a face outside the role system.
268
+ Contexts with their own rule (.card strong = Medium, Demo's
269
+ [&_strong]:font-medium utility) still win on specificity/layer. */
270
+ b,
271
+ strong {
272
+ font-weight: 700;
273
+ }
274
+
256
275
  /* jelinek.css:316. The one flex child that should actually grow — header,
257
276
  chapter-nav and footer keep their natural (content) height; whatever
258
277
  space is left over goes to <main>, pushing the footer down to the
@@ -372,6 +391,9 @@
372
391
  }
373
392
  .serif {
374
393
  font-family: var(--font-serif);
394
+ /* Archer only has Book (400) and Bold — a surrounding Light must not
395
+ be inherited into serif text. */
396
+ font-weight: 400;
375
397
  }
376
398
  .small {
377
399
  font-size: var(--step-down1);
@@ -19,3 +19,47 @@
19
19
  [data-division='mattress'] {
20
20
  --color-accent-text: var(--color-accent-text-mattress);
21
21
  }
22
+
23
+ /* Jelen je jen hnědý, nebo bílý (brand → Znak, Užití s produktovými
24
+ barvami): divizní přetéma přemapovává --JELINEK-BROWN na magentu, ale pro
25
+ jelena je to zakázaný stav — pod divizí Zdravý spánek přechází KAŽDÉ
26
+ obsahové logo na bílou, divizní barvu jelena. Zrcadlí stejné pravidlo
27
+ v jelinek.css u .content-logo. */
28
+ [data-division='mattress'] [data-content-logo],
29
+ [data-theme='mattress'] [data-content-logo] {
30
+ color: var(--color-fg-inverse);
31
+ }
32
+
33
+ /* Skeleton shimmer (majitel, 14. 8. 2026): placeholder při načítání nese
34
+ projíždějící světlý gradient, jak je dnes zvykem — ne jen pulz. Barvy
35
+ jsou tokeny (klidová --bg-gray, projíždějící světlo --bg-white), takže
36
+ se v tmavém režimu překlopí samy. Smyčka 1,5 s je vlastní hodnota:
37
+ pohybové tokeny (--dur-*) popisují přechody stavů, ne nekonečné smyčky.
38
+ Kdo si v systému vypnul animace, dostane klidnou plochu — první kousek
39
+ prefers-reduced-motion v kitu (zásobník: Ohled na omezení pohybu). */
40
+ @keyframes skeleton-shimmer {
41
+ to {
42
+ transform: translateX(100%);
43
+ }
44
+ }
45
+ .skeleton-shimmer {
46
+ position: relative;
47
+ overflow: hidden;
48
+ background: var(--color-surface-gray);
49
+ }
50
+ /* Světelný pás jede přes celou plochu jako vrstva nad ní (transform, ne
51
+ background-position) — na malém řádku i velké fotce je pohyb stejně
52
+ viditelný a nemá hluchá místa. */
53
+ .skeleton-shimmer::after {
54
+ content: '';
55
+ position: absolute;
56
+ inset: 0;
57
+ transform: translateX(-100%);
58
+ background: linear-gradient(90deg, transparent, var(--color-surface) 55%, transparent);
59
+ animation: skeleton-shimmer 1.5s ease-in-out infinite;
60
+ }
61
+ @media (prefers-reduced-motion: reduce) {
62
+ .skeleton-shimmer::after {
63
+ content: none;
64
+ }
65
+ }
@@ -21,13 +21,12 @@
21
21
 
22
22
  Reconciled against assets/css/jelinek.css's @font-face block (the
23
23
  authority for which faces exist): every face declared there is declared
24
- here too, with the same family name, weight and filename. That now
25
- includes Archer Medium (500) — the file had been in assets/fonts/ and in
26
- its README since 03ee9de, but with no @font-face anywhere, so any request
27
- for a 500-weight Archer silently fell back to Book. Declaring it makes
28
- the README true and gives the designer the option of Medium emphasis in
29
- prose; nothing requests it yet (prose <strong> is plain Bold, his
30
- 7cd378c). */
24
+ here too, with the same family name, weight and filename. Only the ROLE
25
+ faces are loaded (majitel, 21. 8. 2026): Walsheim Light/Medium/Bold,
26
+ Archer Book/Bold, Avenir Demi (logo subtitle). Regular, Black, Avenir
27
+ Regular/Bold and Archer Medium stay in assets/fonts/ for other projects,
28
+ but the web never declares them — a stray request falls back and the
29
+ font-faces e2e catches it. */
31
30
  @font-face {
32
31
  font-family: 'GT Walsheim Pro';
33
32
  src: url('/assets/fonts/GT-Walsheim-Pro-Light.woff2') format('woff2');
@@ -35,13 +34,6 @@
35
34
  font-style: normal;
36
35
  font-display: block;
37
36
  }
38
- @font-face {
39
- font-family: 'GT Walsheim Pro';
40
- src: url('/assets/fonts/GT-Walsheim-Pro-Regular.woff2') format('woff2');
41
- font-weight: 400;
42
- font-style: normal;
43
- font-display: block;
44
- }
45
37
  @font-face {
46
38
  font-family: 'GT Walsheim Pro';
47
39
  src: url('/assets/fonts/GT-Walsheim-Pro-Medium.woff2') format('woff2');
@@ -56,20 +48,6 @@
56
48
  font-style: normal;
57
49
  font-display: block;
58
50
  }
59
- @font-face {
60
- font-family: 'GT Walsheim Pro';
61
- src: url('/assets/fonts/GT-Walsheim-Pro-Black.woff2') format('woff2');
62
- font-weight: 900;
63
- font-style: normal;
64
- font-display: block;
65
- }
66
- @font-face {
67
- font-family: 'Avenir Next LT Pro';
68
- src: url('/assets/fonts/AvenirNextLTPro-Regular.woff2') format('woff2');
69
- font-weight: 400;
70
- font-style: normal;
71
- font-display: block;
72
- }
73
51
  @font-face {
74
52
  font-family: 'Avenir Next LT Pro';
75
53
  src: url('/assets/fonts/AvenirNextLTPro-Demi.woff2') format('woff2');
@@ -77,13 +55,6 @@
77
55
  font-style: normal;
78
56
  font-display: block;
79
57
  }
80
- @font-face {
81
- font-family: 'Avenir Next LT Pro';
82
- src: url('/assets/fonts/AvenirNextLTPro-Bold.woff2') format('woff2');
83
- font-weight: 700;
84
- font-style: normal;
85
- font-display: block;
86
- }
87
58
  @font-face {
88
59
  font-family: 'Archer';
89
60
  src: url('/assets/fonts/Archer-Book.woff2') format('woff2');
@@ -91,13 +62,6 @@
91
62
  font-style: normal;
92
63
  font-display: block;
93
64
  }
94
- @font-face {
95
- font-family: 'Archer';
96
- src: url('/assets/fonts/Archer-Medium.woff2') format('woff2');
97
- font-weight: 500;
98
- font-style: normal;
99
- font-display: block;
100
- }
101
65
  @font-face {
102
66
  font-family: 'Archer';
103
67
  src: url('/assets/fonts/Archer-Bold.woff2') format('woff2');
@@ -16,8 +16,8 @@
16
16
  --ON-JELINEK-BLACK: hsl(0, 0%, 95%);
17
17
  --bg-white: hsl(0, 0%, 100%);
18
18
  --bg-gray: hsl(0, 0%, 95%);
19
- --bg-brown: hsl(29, 44%, 95%);
20
- --bg-magenta: hsl(324, 100%, 95%);
19
+ --bg-brown: hsl(27, 58%, 96.3%);
20
+ --bg-magenta: hsl(324, 85%, 92%);
21
21
  --bg-black: hsl(0, 0%, 0%);
22
22
  --border-subtle: hsl(0, 0%, 91%);
23
23
  --text-black: hsl(0, 0%, 5%);
@@ -45,13 +45,20 @@
45
45
  --glass-fill: hsl(0, 0%, 100%, 0.5);
46
46
  --glass-border: hsl(0, 0%, 100%, 0.75);
47
47
  --glass-filter: blur(25px) saturate(140%);
48
- --focus-color: var(--JELINEK-BROWN);
48
+ --overlay-scrim: hsl(0, 0%, 0%, 0.5);
49
+ --overlay-filter: blur(4px);
50
+ --z-bar: 40;
51
+ --z-chapter-nav: 49;
52
+ --z-header: 50;
53
+ --z-overlay: 1200;
54
+ --z-toast: 1500;
55
+ --focus-color: var(--JELINEK-BLACK);
49
56
  --focus-width: 2px;
50
57
  --focus-offset: 2px;
51
- --font-primary: "GT Walsheim Pro", Arial, Helvetica, sans-serif;
58
+ --font-primary: "GT Walsheim Pro", "Arial Black", Arial, Helvetica, sans-serif;
52
59
  --font-secondary: "GT Walsheim Pro", Arial, Helvetica, sans-serif;
53
- --font-serif: "Archer", Georgia, "Times New Roman", serif;
54
- --page-max: 1180px;
60
+ --font-serif: "Archer", Baskerville, "Baskerville Old Face", Georgia, "Times New Roman", serif;
61
+ --page-max: 1480px;
55
62
  --header-h: 70px;
56
63
  --subnav-h: 58px;
57
64
  --step0: 1rem;
@@ -82,8 +89,8 @@
82
89
  .dark {
83
90
  --bg-white: hsl(0, 0%, 5%);
84
91
  --bg-gray: hsl(0, 0%, 15%);
85
- --bg-brown: hsl(29, 44%, 10%);
86
- --bg-magenta: hsl(324, 100%, 10%);
92
+ --bg-brown: hsl(27, 58%, 10%);
93
+ --bg-magenta: hsl(324, 85%, 10%);
87
94
  --bg-black: hsl(0, 0%, 0%);
88
95
  --text-black: hsl(0, 0%, 92%);
89
96
  --text-muted1: hsl(0, 0%, 62%);
@@ -105,7 +112,7 @@
105
112
  --sem-error-strong: hsl(5, 100%, 54%);
106
113
  --glass-fill: hsl(0, 0%, 0%, 0.6);
107
114
  --glass-border: hsl(0, 0%, 100%, 0.25);
108
- --focus-color: var(--JELINEK-BROWN-HIGHLIGHT);
115
+ --focus-color: var(--JELINEK-BLACK);
109
116
  --border-subtle: hsl(0, 0%, 20%);
110
117
  --border-highlight-white: hsl(0, 0%, 40%);
111
118
  --shadow-interactive: 0px 4px 4px hsl(0, 0%, 0%, 0.063), 0px 12px 12px hsl(0, 0%, 0%, 0.188);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jelinek/ui",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "JELÍNEK Svelte 5 component kit, generated from the brand guide",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",