@jelinek/ui 0.5.3 → 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.
- package/dist/components/ChapterNav.svelte +1 -1
- package/dist/components/Checkbox.svelte +13 -15
- package/dist/components/Checkbox.svelte.d.ts +1 -0
- package/dist/components/ContentLogo.svelte +18 -13
- package/dist/components/ContentLogo.svelte.d.ts +6 -5
- package/dist/components/Grid.svelte +9 -5
- package/dist/components/Label.svelte +12 -1
- package/dist/components/Label.svelte.d.ts +8 -0
- package/dist/components/PageHero.svelte +36 -2
- package/dist/components/PageHero.svelte.d.ts +9 -0
- package/dist/components/Pager.svelte +5 -2
- package/dist/components/PhotoTile.svelte +4 -2
- package/dist/components/ProductCard.svelte +66 -44
- package/dist/components/ProductCard.svelte.d.ts +17 -4
- package/dist/components/Radio.svelte +12 -8
- package/dist/components/Radio.svelte.d.ts +1 -0
- package/dist/components/Select.svelte +70 -12
- package/dist/components/Select.svelte.d.ts +11 -0
- package/dist/components/SiteFooter.svelte +19 -0
- package/dist/components/SiteFooter.svelte.d.ts +8 -0
- package/dist/components/SiteHeader.svelte +11 -9
- package/dist/components/Slider.svelte +6 -11
- package/dist/components/Switch.svelte +76 -0
- package/dist/components/Switch.svelte.d.ts +12 -0
- package/dist/components/TextField.svelte +61 -10
- package/dist/components/TextField.svelte.d.ts +13 -0
- package/dist/components/Textarea.svelte +92 -0
- package/dist/components/Textarea.svelte.d.ts +23 -0
- package/dist/components/Tile.svelte +12 -11
- package/dist/components/Tile.svelte.d.ts +8 -2
- package/dist/components/blocks/CmsButton.svelte +36 -0
- package/dist/components/blocks/CmsButton.svelte.d.ts +12 -0
- package/dist/components/blocks/CmsColumns.svelte +20 -0
- package/dist/components/blocks/CmsColumns.svelte.d.ts +9 -0
- package/dist/components/blocks/CmsCover.svelte +71 -0
- package/dist/components/blocks/CmsCover.svelte.d.ts +17 -0
- package/dist/components/blocks/CmsHeading.svelte +50 -0
- package/dist/components/blocks/CmsHeading.svelte.d.ts +14 -0
- package/dist/components/blocks/CmsImage.svelte +43 -0
- package/dist/components/blocks/CmsImage.svelte.d.ts +13 -0
- package/dist/components/blocks/CmsMediaText.svelte +64 -0
- package/dist/components/blocks/CmsMediaText.svelte.d.ts +16 -0
- package/dist/components/blocks/CmsText.svelte +53 -0
- package/dist/components/blocks/CmsText.svelte.d.ts +10 -0
- package/dist/components/commerce/Breadcrumbs.svelte +41 -0
- package/dist/components/commerce/Breadcrumbs.svelte.d.ts +11 -0
- package/dist/components/commerce/CartDrawer.svelte +131 -0
- package/dist/components/commerce/CartDrawer.svelte.d.ts +37 -0
- package/dist/components/commerce/CartLineItem.svelte +67 -0
- package/dist/components/commerce/CartLineItem.svelte.d.ts +14 -0
- package/dist/components/commerce/CheckoutProgress.svelte +49 -0
- package/dist/components/commerce/CheckoutProgress.svelte.d.ts +11 -0
- package/dist/components/commerce/ConfigSection.svelte +24 -0
- package/dist/components/commerce/ConfigSection.svelte.d.ts +11 -0
- package/dist/components/commerce/DimensionSlider.svelte +86 -0
- package/dist/components/commerce/DimensionSlider.svelte.d.ts +18 -0
- package/dist/components/commerce/Drawer.svelte +81 -0
- package/dist/components/commerce/Drawer.svelte.d.ts +12 -0
- package/dist/components/commerce/EmptyState.svelte +37 -0
- package/dist/components/commerce/EmptyState.svelte.d.ts +13 -0
- package/dist/components/commerce/FilterChip.svelte +35 -0
- package/dist/components/commerce/FilterChip.svelte.d.ts +10 -0
- package/dist/components/commerce/FreeShippingBar.svelte +63 -0
- package/dist/components/commerce/FreeShippingBar.svelte.d.ts +12 -0
- package/dist/components/commerce/Gallery.svelte +194 -0
- package/dist/components/commerce/Gallery.svelte.d.ts +27 -0
- package/dist/components/commerce/ListingToolbar.svelte +62 -0
- package/dist/components/commerce/ListingToolbar.svelte.d.ts +19 -0
- package/dist/components/commerce/Modal.svelte +73 -0
- package/dist/components/commerce/Modal.svelte.d.ts +11 -0
- package/dist/components/commerce/OrderSummary.svelte +85 -0
- package/dist/components/commerce/OrderSummary.svelte.d.ts +25 -0
- package/dist/components/commerce/Pagination.svelte +115 -0
- package/dist/components/commerce/Pagination.svelte.d.ts +14 -0
- package/dist/components/commerce/PriceDisplay.svelte +41 -0
- package/dist/components/commerce/PriceDisplay.svelte.d.ts +14 -0
- package/dist/components/commerce/ProductHeader.svelte +62 -0
- package/dist/components/commerce/ProductHeader.svelte.d.ts +21 -0
- package/dist/components/commerce/ProductStickyBar.svelte +42 -0
- package/dist/components/commerce/ProductStickyBar.svelte.d.ts +14 -0
- package/dist/components/commerce/ProductTabs.svelte +87 -0
- package/dist/components/commerce/ProductTabs.svelte.d.ts +18 -0
- package/dist/components/commerce/QuantityStepper.svelte +44 -0
- package/dist/components/commerce/QuantityStepper.svelte.d.ts +13 -0
- package/dist/components/commerce/RatingStars.svelte +39 -0
- package/dist/components/commerce/RatingStars.svelte.d.ts +9 -0
- package/dist/components/commerce/SearchField.svelte +92 -0
- package/dist/components/commerce/SearchField.svelte.d.ts +15 -0
- package/dist/components/commerce/SideToggle.svelte +79 -0
- package/dist/components/commerce/SideToggle.svelte.d.ts +13 -0
- package/dist/components/commerce/Skeleton.svelte +13 -0
- package/dist/components/commerce/Skeleton.svelte.d.ts +7 -0
- package/dist/components/commerce/StickyBar.svelte +27 -0
- package/dist/components/commerce/StickyBar.svelte.d.ts +8 -0
- package/dist/components/commerce/StockBadge.svelte +45 -0
- package/dist/components/commerce/StockBadge.svelte.d.ts +9 -0
- package/dist/components/commerce/Toaster.svelte +38 -0
- package/dist/components/commerce/Toaster.svelte.d.ts +7 -0
- package/dist/components/commerce/TrustBadges.svelte +26 -0
- package/dist/components/commerce/TrustBadges.svelte.d.ts +16 -0
- package/dist/components/commerce/cart-types.d.ts +12 -0
- package/dist/components/commerce/cart-types.js +1 -0
- package/dist/components/commerce/money.d.ts +14 -0
- package/dist/components/commerce/money.js +26 -0
- package/dist/components/commerce/overlay.d.ts +26 -0
- package/dist/components/commerce/overlay.js +69 -0
- package/dist/components/commerce/toast.d.ts +26 -0
- package/dist/components/commerce/toast.js +44 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +41 -0
- package/dist/theme/base.css +56 -7
- package/dist/theme/extras.css +63 -0
- package/dist/theme/fonts.css +6 -42
- package/dist/theme/tokens.css +16 -9
- package/package.json +6 -2
|
@@ -3,12 +3,17 @@
|
|
|
3
3
|
import type { HTMLAnchorAttributes, HTMLAttributes } from 'svelte/elements';
|
|
4
4
|
import { cn } from '../utils/cn.js';
|
|
5
5
|
|
|
6
|
-
type Color = 'brown' | 'magenta';
|
|
7
|
-
|
|
8
6
|
interface Props {
|
|
9
7
|
href?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Vybraná dlaždice: silný černý rámeček (2px, padding to kompenzuje,
|
|
10
|
+
* dlaždice nezmění rozměr). Bez rozlišení divize — rozhodnutí majitele
|
|
11
|
+
* (14. 8. 2026), stejná neutrální řeč jako formuláře a prstenec
|
|
12
|
+
* fokusu; --JELINEK-BLACK se v tmavém režimu překlopí na bílou.
|
|
13
|
+
* Dřívější `color` prop (hnědá/magenta výplň) zanikl s tímto
|
|
14
|
+
* rozhodnutím.
|
|
15
|
+
*/
|
|
10
16
|
active?: boolean;
|
|
11
|
-
color?: Color;
|
|
12
17
|
class?: string;
|
|
13
18
|
children: Snippet;
|
|
14
19
|
}
|
|
@@ -16,16 +21,12 @@
|
|
|
16
21
|
let {
|
|
17
22
|
href,
|
|
18
23
|
active = false,
|
|
19
|
-
color = 'brown',
|
|
20
24
|
class: className,
|
|
21
25
|
children,
|
|
22
26
|
...rest
|
|
23
27
|
}: Props & Omit<HTMLAnchorAttributes & HTMLAttributes<HTMLDivElement>, 'href'> = $props();
|
|
24
28
|
|
|
25
|
-
const ACTIVE
|
|
26
|
-
brown: 'border-jelinek-brown bg-surface-brown',
|
|
27
|
-
magenta: 'border-jelinek-magenta bg-surface-magenta'
|
|
28
|
-
};
|
|
29
|
+
const ACTIVE = 'border-2 border-jelinek-black p-[21px]';
|
|
29
30
|
|
|
30
31
|
// jelinek.css:474-481 (shared surface, --box-accent unset by .tile so it
|
|
31
32
|
// defaults to --border-subtle at both gradient stops) collapses to a flat
|
|
@@ -68,8 +69,8 @@
|
|
|
68
69
|
const BASE =
|
|
69
70
|
'block rounded-card border border-border-subtle bg-surface p-[22px] ' +
|
|
70
71
|
'font-secondary font-light text-fg no-underline ' +
|
|
71
|
-
'transition-[box-shadow,
|
|
72
|
-
'shadow-resting hover
|
|
72
|
+
'transition-[box-shadow,border-color] duration-[var(--dur-base)] ease-[var(--ease)] ' +
|
|
73
|
+
'shadow-resting hover:shadow-interactive hover:border-jelinek-black ' +
|
|
73
74
|
'[&_h3]:mb-1.5 [&_h3]:text-step1 [&_h3]:leading-[1.3] ' +
|
|
74
75
|
'[&_p]:m-0 [&_p]:text-[0.9rem] [&_p]:text-fg-muted1 ' +
|
|
75
76
|
'[&_strong]:font-medium [&_b]:font-medium ' +
|
|
@@ -105,7 +106,7 @@
|
|
|
105
106
|
// .tile (jelinek.css:917-928) declares no cursor of its own, so this is a
|
|
106
107
|
// kit-only addition, not a reproduction — the css-lock gate watches the
|
|
107
108
|
// guide's rule text and is unaffected.
|
|
108
|
-
const classes = $derived(cn(BASE, href && 'cursor-pointer', active && ACTIVE
|
|
109
|
+
const classes = $derived(cn(BASE, href && 'cursor-pointer', active && ACTIVE, className));
|
|
109
110
|
</script>
|
|
110
111
|
|
|
111
112
|
{#if href}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { HTMLAnchorAttributes, HTMLAttributes } from 'svelte/elements';
|
|
3
|
-
type Color = 'brown' | 'magenta';
|
|
4
3
|
interface Props {
|
|
5
4
|
href?: string;
|
|
5
|
+
/**
|
|
6
|
+
* Vybraná dlaždice: silný černý rámeček (2px, padding to kompenzuje,
|
|
7
|
+
* dlaždice nezmění rozměr). Bez rozlišení divize — rozhodnutí majitele
|
|
8
|
+
* (14. 8. 2026), stejná neutrální řeč jako formuláře a prstenec
|
|
9
|
+
* fokusu; --JELINEK-BLACK se v tmavém režimu překlopí na bílou.
|
|
10
|
+
* Dřívější `color` prop (hnědá/magenta výplň) zanikl s tímto
|
|
11
|
+
* rozhodnutím.
|
|
12
|
+
*/
|
|
6
13
|
active?: boolean;
|
|
7
|
-
color?: Color;
|
|
8
14
|
class?: string;
|
|
9
15
|
children: Snippet;
|
|
10
16
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Button from '../Button.svelte';
|
|
3
|
+
import { cn } from '../../utils/cn.js';
|
|
4
|
+
|
|
5
|
+
// Blok CMS „Tlačítko": ZADÁNÍ VÝSLOVNĚ ŘÍKÁ „musí se sjednotit s tlačítky
|
|
6
|
+
// z UI guide, ne navrhovat znovu" — blok je proto jen zarovnávací obal
|
|
7
|
+
// nad komponentou Button. Varianty a barvy jsou Buttonu vlastní.
|
|
8
|
+
interface Props {
|
|
9
|
+
/** Text tlačítka. */
|
|
10
|
+
label: string;
|
|
11
|
+
href: string;
|
|
12
|
+
align?: 'left' | 'center' | 'right';
|
|
13
|
+
variant?: 'primary' | 'secondary';
|
|
14
|
+
color?: 'brown' | 'magenta' | 'black';
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let {
|
|
19
|
+
label,
|
|
20
|
+
href,
|
|
21
|
+
align = 'left',
|
|
22
|
+
variant = 'primary',
|
|
23
|
+
color = 'brown',
|
|
24
|
+
class: className
|
|
25
|
+
}: Props = $props();
|
|
26
|
+
|
|
27
|
+
const ALIGN: Record<string, string> = {
|
|
28
|
+
left: 'justify-start',
|
|
29
|
+
center: 'justify-center',
|
|
30
|
+
right: 'justify-end'
|
|
31
|
+
};
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
<div class={cn('flex', ALIGN[align], className)}>
|
|
35
|
+
<Button {href} {variant} {color}>{label}</Button>
|
|
36
|
+
</div>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Text tlačítka. */
|
|
3
|
+
label: string;
|
|
4
|
+
href: string;
|
|
5
|
+
align?: 'left' | 'center' | 'right';
|
|
6
|
+
variant?: 'primary' | 'secondary';
|
|
7
|
+
color?: 'brown' | 'magenta' | 'black';
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const CmsButton: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type CmsButton = ReturnType<typeof CmsButton>;
|
|
12
|
+
export default CmsButton;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import Grid from '../Grid.svelte';
|
|
4
|
+
|
|
5
|
+
// Blok CMS „Sloupce": 2–4 sloupce, do kterých redaktor vkládá jiné bloky
|
|
6
|
+
// (o jednu úroveň, bez vnořování kontejnerů). Skládání na mobilu řeší
|
|
7
|
+
// PŘESNĚ mřížka guidu (Grid): 1 sloupec, od 640 px 2, od 1024 px plný
|
|
8
|
+
// počet — odpověď na otázku zadání „kdy 4 přejdou na 2 a kdy na 1".
|
|
9
|
+
interface Props {
|
|
10
|
+
cols?: 2 | 3 | 4;
|
|
11
|
+
children: Snippet;
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let { cols = 2, children, class: className }: Props = $props();
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<Grid {cols} class={className}>
|
|
19
|
+
{@render children()}
|
|
20
|
+
</Grid>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
cols?: 2 | 3 | 4;
|
|
4
|
+
children: Snippet;
|
|
5
|
+
class?: string;
|
|
6
|
+
}
|
|
7
|
+
declare const CmsColumns: import("svelte").Component<Props, {}, "">;
|
|
8
|
+
type CmsColumns = ReturnType<typeof CmsColumns>;
|
|
9
|
+
export default CmsColumns;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Button from '../Button.svelte';
|
|
3
|
+
import { cn } from '../../utils/cn.js';
|
|
4
|
+
|
|
5
|
+
// Blok CMS „Úvodní cover": celoplošná fotka s H1, nadtitulkem, textem
|
|
6
|
+
// a CTA. Vzor povýšený z dema (hero kategorie Nábytek z masivu).
|
|
7
|
+
// ČITELNOST na světlé i tmavé fotce řeší vždy přítomný gradient odspoda
|
|
8
|
+
// (černá 60 % → průhledná) + bílý text — funguje na obou, žádné
|
|
9
|
+
// rozhodování redaktora. Na mobilu je fotka na výšku a text sedí dole
|
|
10
|
+
// nad gradientem, takže se s fotkou nepere.
|
|
11
|
+
interface Props {
|
|
12
|
+
image: string;
|
|
13
|
+
/** Povinný alt fotky (prázdný jen u čistě dekorativního pozadí). */
|
|
14
|
+
imageAlt?: string;
|
|
15
|
+
/** Hlavní nadpis stránky (H1). */
|
|
16
|
+
title: string;
|
|
17
|
+
eyebrow?: string;
|
|
18
|
+
text?: string;
|
|
19
|
+
ctaLabel?: string;
|
|
20
|
+
ctaHref?: string;
|
|
21
|
+
/** Volba výšky ze zadání bloku. */
|
|
22
|
+
height?: 'nizky' | 'stredni' | 'vysoky';
|
|
23
|
+
class?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let {
|
|
27
|
+
image,
|
|
28
|
+
imageAlt = '',
|
|
29
|
+
title,
|
|
30
|
+
eyebrow,
|
|
31
|
+
text,
|
|
32
|
+
ctaLabel,
|
|
33
|
+
ctaHref,
|
|
34
|
+
height = 'stredni',
|
|
35
|
+
class: className
|
|
36
|
+
}: Props = $props();
|
|
37
|
+
|
|
38
|
+
const HEIGHT: Record<string, string> = {
|
|
39
|
+
nizky: 'min-h-[320px]',
|
|
40
|
+
stredni: 'min-h-[440px]',
|
|
41
|
+
vysoky: 'min-h-[70dvh]'
|
|
42
|
+
};
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<section class={cn('relative flex items-end overflow-hidden', HEIGHT[height], className)}>
|
|
46
|
+
<img src={image} alt={imageAlt} class="absolute inset-0 h-full w-full object-cover" />
|
|
47
|
+
<div
|
|
48
|
+
class="absolute inset-0 bg-gradient-to-t from-black/60 via-black/20 to-transparent"
|
|
49
|
+
></div>
|
|
50
|
+
<div class="relative mx-auto w-full max-w-page px-6 pt-24 pb-10">
|
|
51
|
+
{#if eyebrow}
|
|
52
|
+
<!-- Kicker z typografie guidu (tučný, --step2, bez verzálek —
|
|
53
|
+
majitel, 21. 8. 2026); na fotce nese bílou místo hnědé. -->
|
|
54
|
+
<p class="m-0 font-secondary text-step2 font-bold text-white/85">
|
|
55
|
+
{eyebrow}
|
|
56
|
+
</p>
|
|
57
|
+
{/if}
|
|
58
|
+
<h1 class="mt-1 mb-0 font-primary text-step4 font-bold text-white [text-wrap:balance]">
|
|
59
|
+
{title}
|
|
60
|
+
</h1>
|
|
61
|
+
{#if text}
|
|
62
|
+
<!-- Perex je čtený text — Archer, jako perex v PageHero. -->
|
|
63
|
+
<p class="mt-3 mb-0 max-w-[560px] font-serif font-normal text-white/90">{text}</p>
|
|
64
|
+
{/if}
|
|
65
|
+
{#if ctaLabel && ctaHref}
|
|
66
|
+
<div class="mt-5">
|
|
67
|
+
<Button href={ctaHref}>{ctaLabel}</Button>
|
|
68
|
+
</div>
|
|
69
|
+
{/if}
|
|
70
|
+
</div>
|
|
71
|
+
</section>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
image: string;
|
|
3
|
+
/** Povinný alt fotky (prázdný jen u čistě dekorativního pozadí). */
|
|
4
|
+
imageAlt?: string;
|
|
5
|
+
/** Hlavní nadpis stránky (H1). */
|
|
6
|
+
title: string;
|
|
7
|
+
eyebrow?: string;
|
|
8
|
+
text?: string;
|
|
9
|
+
ctaLabel?: string;
|
|
10
|
+
ctaHref?: string;
|
|
11
|
+
/** Volba výšky ze zadání bloku. */
|
|
12
|
+
height?: 'nizky' | 'stredni' | 'vysoky';
|
|
13
|
+
class?: string;
|
|
14
|
+
}
|
|
15
|
+
declare const CmsCover: import("svelte").Component<Props, {}, "">;
|
|
16
|
+
type CmsCover = ReturnType<typeof CmsCover>;
|
|
17
|
+
export default CmsCover;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../../utils/cn.js';
|
|
3
|
+
|
|
4
|
+
// Blok CMS „Nadpis" (docs/chybejici-bloky-cms.md): nadtitulek + nadpis
|
|
5
|
+
// úrovně h2–h4. Vzhled = typografie guidu: eyebrow, nadpisová škála,
|
|
6
|
+
// podtržení. Podtržení patří jen nad text (pravidlo majitele) — a jestli
|
|
7
|
+
// za blokem následuje textový blok, ví jen CMS renderer, proto to řídí
|
|
8
|
+
// prop `rule` (výchozí zapnuto: nadpis v obsahu textem obvykle pokračuje;
|
|
9
|
+
// h4 podtržení nemá nikdy, stejně jako v guidu).
|
|
10
|
+
interface Props {
|
|
11
|
+
/** Text nadpisu. */
|
|
12
|
+
text: string;
|
|
13
|
+
/** Nadtitulek (eyebrow) nad nadpisem. */
|
|
14
|
+
eyebrow?: string;
|
|
15
|
+
/** Úroveň nadpisu v osnově stránky. */
|
|
16
|
+
level?: 'h2' | 'h3' | 'h4';
|
|
17
|
+
/** Podtržení pod nadpisem — vypnout, když za blokem NEnásleduje text. */
|
|
18
|
+
rule?: boolean;
|
|
19
|
+
class?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let { text, eyebrow, level = 'h2', rule = true, class: className }: Props = $props();
|
|
23
|
+
|
|
24
|
+
// Velikosti podle typografické škály guidu (h2 step3, h3 step2, h4 step1
|
|
25
|
+
// — stejné hodnoty používají kapitoly kitu).
|
|
26
|
+
const SIZE: Record<'h2' | 'h3' | 'h4', string> = {
|
|
27
|
+
h2: 'text-step5',
|
|
28
|
+
h3: 'text-step4',
|
|
29
|
+
h4: 'text-step3'
|
|
30
|
+
};
|
|
31
|
+
// Nadtitulek = kicker z typografie guidu (brand → Typografie, hierarchie:
|
|
32
|
+
// „Kicker — GT Walsheim Bold, produktová barva“; jelinek.css .eyebrow):
|
|
33
|
+
// tučný, hnědý, velikost --step2, BEZ verzálek — verzálky jsou vyhrazené
|
|
34
|
+
// menu a krátkým titulkům a dřívější uppercase tu byl proti manuálu
|
|
35
|
+
// (majitel, 21. 8. 2026). Stejné třídy nese komponenta Eyebrow.
|
|
36
|
+
const EYEBROW = 'font-secondary text-step2 font-bold text-jelinek-brown';
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<div class={cn('max-w-[720px]', className)}>
|
|
40
|
+
{#if eyebrow}<p class="m-0 {EYEBROW}">{eyebrow}</p>{/if}
|
|
41
|
+
<svelte:element
|
|
42
|
+
this={level}
|
|
43
|
+
class="mt-1 mb-0 font-primary {SIZE[level]} font-bold text-fg [text-wrap:balance]"
|
|
44
|
+
>
|
|
45
|
+
{text}
|
|
46
|
+
</svelte:element>
|
|
47
|
+
{#if rule && level !== 'h4'}
|
|
48
|
+
<hr class="rule" />
|
|
49
|
+
{/if}
|
|
50
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Text nadpisu. */
|
|
3
|
+
text: string;
|
|
4
|
+
/** Nadtitulek (eyebrow) nad nadpisem. */
|
|
5
|
+
eyebrow?: string;
|
|
6
|
+
/** Úroveň nadpisu v osnově stránky. */
|
|
7
|
+
level?: 'h2' | 'h3' | 'h4';
|
|
8
|
+
/** Podtržení pod nadpisem — vypnout, když za blokem NEnásleduje text. */
|
|
9
|
+
rule?: boolean;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
declare const CmsHeading: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type CmsHeading = ReturnType<typeof CmsHeading>;
|
|
14
|
+
export default CmsHeading;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../../utils/cn.js';
|
|
3
|
+
|
|
4
|
+
// Blok CMS „Obrázek": fotka z mediální knihovny s POVINNÝM altem
|
|
5
|
+
// (přístupnost) a volitelným popiskem. Poměr stran si blok vynucuje sám
|
|
6
|
+
// (výchozí 3:2 s ořezem cover) — knihovna nezaručuje žádný poměr, a bez
|
|
7
|
+
// ořezu by velmi vysoká fotka rozbila rytmus stránky. `ratio="auto"`
|
|
8
|
+
// nechá přirozený poměr, když na něm záleží (např. pérovka).
|
|
9
|
+
interface Props {
|
|
10
|
+
src: string;
|
|
11
|
+
/** Povinný alternativní text — bez něj obrázek do CMS nepatří. */
|
|
12
|
+
alt: string;
|
|
13
|
+
/** Popisek pod obrázkem. */
|
|
14
|
+
caption?: string;
|
|
15
|
+
/** Vynucený poměr stran; „auto" = přirozený poměr fotky. */
|
|
16
|
+
ratio?: '3:2' | '4:3' | '16:9' | '1:1' | 'auto';
|
|
17
|
+
class?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let { src, alt, caption, ratio = '3:2', class: className }: Props = $props();
|
|
21
|
+
|
|
22
|
+
const RATIO: Record<string, string> = {
|
|
23
|
+
'3:2': 'aspect-[3/2] object-cover',
|
|
24
|
+
'4:3': 'aspect-[4/3] object-cover',
|
|
25
|
+
'16:9': 'aspect-video object-cover',
|
|
26
|
+
'1:1': 'aspect-square object-cover',
|
|
27
|
+
auto: ''
|
|
28
|
+
};
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<figure class={cn('m-0', className)}>
|
|
32
|
+
<img
|
|
33
|
+
{src}
|
|
34
|
+
{alt}
|
|
35
|
+
loading="lazy"
|
|
36
|
+
class="w-full rounded-image shadow-resting {RATIO[ratio]}"
|
|
37
|
+
/>
|
|
38
|
+
{#if caption}
|
|
39
|
+
<figcaption class="mt-2 font-secondary text-step-down1 font-light text-fg-muted1">
|
|
40
|
+
{caption}
|
|
41
|
+
</figcaption>
|
|
42
|
+
{/if}
|
|
43
|
+
</figure>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
src: string;
|
|
3
|
+
/** Povinný alternativní text — bez něj obrázek do CMS nepatří. */
|
|
4
|
+
alt: string;
|
|
5
|
+
/** Popisek pod obrázkem. */
|
|
6
|
+
caption?: string;
|
|
7
|
+
/** Vynucený poměr stran; „auto" = přirozený poměr fotky. */
|
|
8
|
+
ratio?: '3:2' | '4:3' | '16:9' | '1:1' | 'auto';
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
declare const CmsImage: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type CmsImage = ReturnType<typeof CmsImage>;
|
|
13
|
+
export default CmsImage;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { cn } from '../../utils/cn.js';
|
|
4
|
+
|
|
5
|
+
// Blok CMS „Obrázek + text": fotka vedle obsahu (obsah = další bloky,
|
|
6
|
+
// typicky Nadpis + Text + Tlačítko — kontejner o jedné úrovni vnoření,
|
|
7
|
+
// přesně podle zadání). Vzor povýšený z dema. NA MOBILU JE FOTKA VŽDY
|
|
8
|
+
// NAHOŘE — web stojí na fotkách, fotka prodává a text vysvětluje
|
|
9
|
+
// (odpověď na otevřenou otázku zadání).
|
|
10
|
+
interface Props {
|
|
11
|
+
image: string;
|
|
12
|
+
imageAlt: string;
|
|
13
|
+
/** Strana obrázku na desktopu. */
|
|
14
|
+
side?: 'left' | 'right';
|
|
15
|
+
/** Poměr stran fotky. */
|
|
16
|
+
ratio?: '3:2' | '4:3' | '1:1';
|
|
17
|
+
/** Fotka od kraje ke kraji (bez rádiusu a bočního okraje). */
|
|
18
|
+
fullBleed?: boolean;
|
|
19
|
+
children: Snippet;
|
|
20
|
+
class?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let {
|
|
24
|
+
image,
|
|
25
|
+
imageAlt,
|
|
26
|
+
side = 'right',
|
|
27
|
+
ratio = '4:3',
|
|
28
|
+
fullBleed = false,
|
|
29
|
+
children,
|
|
30
|
+
class: className
|
|
31
|
+
}: Props = $props();
|
|
32
|
+
|
|
33
|
+
const RATIO: Record<string, string> = {
|
|
34
|
+
'3:2': 'aspect-[3/2]',
|
|
35
|
+
'4:3': 'aspect-[4/3]',
|
|
36
|
+
'1:1': 'aspect-square'
|
|
37
|
+
};
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
{#if fullBleed}
|
|
41
|
+
<!-- Varianta na celou šířku: fotka bez okrajů, text v kontejneru pod ní
|
|
42
|
+
(na desktopu překrývá spodní část fotky nepotřebujeme — čitelnost). -->
|
|
43
|
+
<section class={cn('', className)}>
|
|
44
|
+
<img src={image} alt={imageAlt} class="w-full {RATIO[ratio]} object-cover" />
|
|
45
|
+
<div class="mx-auto w-full max-w-page px-6 pt-8">
|
|
46
|
+
{@render children()}
|
|
47
|
+
</div>
|
|
48
|
+
</section>
|
|
49
|
+
{:else}
|
|
50
|
+
<section
|
|
51
|
+
class={cn('grid items-center gap-8 min-[1024px]:grid-cols-2 min-[1024px]:gap-10', className)}
|
|
52
|
+
>
|
|
53
|
+
<!-- Mobil: fotka první (DOM pořadí). Desktop: strana podle `side`. -->
|
|
54
|
+
<img
|
|
55
|
+
src={image}
|
|
56
|
+
alt={imageAlt}
|
|
57
|
+
loading="lazy"
|
|
58
|
+
class="w-full rounded-image {RATIO[ratio]} object-cover shadow-resting {side === 'right'
|
|
59
|
+
? 'min-[1024px]:order-last'
|
|
60
|
+
: ''}"
|
|
61
|
+
/>
|
|
62
|
+
<div>{@render children()}</div>
|
|
63
|
+
</section>
|
|
64
|
+
{/if}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
image: string;
|
|
4
|
+
imageAlt: string;
|
|
5
|
+
/** Strana obrázku na desktopu. */
|
|
6
|
+
side?: 'left' | 'right';
|
|
7
|
+
/** Poměr stran fotky. */
|
|
8
|
+
ratio?: '3:2' | '4:3' | '1:1';
|
|
9
|
+
/** Fotka od kraje ke kraji (bez rádiusu a bočního okraje). */
|
|
10
|
+
fullBleed?: boolean;
|
|
11
|
+
children: Snippet;
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
declare const CmsMediaText: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type CmsMediaText = ReturnType<typeof CmsMediaText>;
|
|
16
|
+
export default CmsMediaText;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { cn } from '../../utils/cn.js';
|
|
4
|
+
|
|
5
|
+
// Blok CMS „Text": formátovaný text redaktora — odstavce, odrážkové
|
|
6
|
+
// i číslované seznamy, odkazy, tučné. KURZÍVA SE NEPOUŽÍVÁ NIKDY
|
|
7
|
+
// (rozhodnutí majitele 14. 8. 2026) — kdyby ji CMS přece jen poslalo,
|
|
8
|
+
// blok ji narovná na běžný řez, aby se zákaz nedal obejít obsahem.
|
|
9
|
+
// Maximální šířka řádku 720 px drží čitelnost (rozvrzeni#sirka).
|
|
10
|
+
interface Props {
|
|
11
|
+
/** Hotové HTML z CMS. Alternativně jde obsah předat jako children. */
|
|
12
|
+
html?: string;
|
|
13
|
+
children?: Snippet;
|
|
14
|
+
class?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let { html, children, class: className }: Props = $props();
|
|
18
|
+
|
|
19
|
+
// PÍSMO: Archer (font-serif) — brand → Typografie: „Odstavec — Archer
|
|
20
|
+
// Book. Plynulý čtený text pro popisy a delší obsah mimo ohraničené
|
|
21
|
+
// bloky.“ Dřívější GT Walsheim Light tu byl proti manuálu (majitel,
|
|
22
|
+
// 21. 8. 2026); Walsheim Light patří drobnému textu a textům
|
|
23
|
+
// v ohraničených blocích, ne odstavcům obsahu.
|
|
24
|
+
const TEXT =
|
|
25
|
+
'max-w-[720px] font-serif font-normal text-step0 text-fg ' +
|
|
26
|
+
// odstavce a mezery mezi nimi (guide: p margin 0 0 1em, line-height 1.4)
|
|
27
|
+
'[&_p]:my-0 [&_p]:mb-[1em] [&_p]:leading-[1.4] [&_p:last-child]:mb-0 ' +
|
|
28
|
+
// seznamy — odrážky i číslování, s odsazením
|
|
29
|
+
'[&_ul]:my-3 [&_ul]:list-disc [&_ul]:pl-6 ' +
|
|
30
|
+
'[&_ol]:my-3 [&_ol]:list-decimal [&_ol]:pl-6 ' +
|
|
31
|
+
'[&_li]:my-1 ' +
|
|
32
|
+
// odkaz uvnitř odstavce — tučný, akcent, podtržení až na hover (guide a{})
|
|
33
|
+
'[&_a]:font-bold [&_a]:text-jelinek-brown [&_a]:no-underline hover:[&_a]:underline ' +
|
|
34
|
+
// tučné je jediné povolené zvýraznění (Archer Bold je v sadě). Suffix
|
|
35
|
+
// `!`: Demo.svelte kreslí ukázkové panely s [&_strong]:font-medium
|
|
36
|
+
// (Walsheim konvence panelů) a jeho pravidlo by při stejné specificitě
|
|
37
|
+
// vyhrálo pořadím ve stylopisu — blok uvnitř ukázky by měl Archer
|
|
38
|
+
// Medium, zakázaný řez. Bez vnořeného obsahu v Demu by ! nebylo třeba.
|
|
39
|
+
'[&_strong]:font-bold! [&_b]:font-bold! ' +
|
|
40
|
+
// …kurzívu narovnáváme (zákaz platí i pro obsah z CMS)
|
|
41
|
+
'[&_em]:not-italic [&_i]:not-italic';
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<div class={cn(TEXT, className)}>
|
|
45
|
+
{#if html}
|
|
46
|
+
<!-- Obsah přichází z vlastního CMS (redakční vstup), ne od anonymních
|
|
47
|
+
uživatelů — sanitizace je věc aplikace. -->
|
|
48
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
49
|
+
{@html html}
|
|
50
|
+
{:else if children}
|
|
51
|
+
{@render children()}
|
|
52
|
+
{/if}
|
|
53
|
+
</div>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Hotové HTML z CMS. Alternativně jde obsah předat jako children. */
|
|
4
|
+
html?: string;
|
|
5
|
+
children?: Snippet;
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
declare const CmsText: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type CmsText = ReturnType<typeof CmsText>;
|
|
10
|
+
export default CmsText;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Task 48 — UNIFIED WITH THE GUIDE'S BREADCRUMBS (the owner's spec, same
|
|
3
|
+
// treatment PageHero's `crumbs` and the hand-written pages' `.breadcrumbs`
|
|
4
|
+
// carry): a small Avenir UI row, "/" separators in muted2, ancestor links
|
|
5
|
+
// muted darkening on hover, the current page in full text colour — never
|
|
6
|
+
// the accent. The previous "›" + accent-coloured current item was this
|
|
7
|
+
// component's own invention and made the e-shop's trail a different design
|
|
8
|
+
// from the site's.
|
|
9
|
+
interface Props {
|
|
10
|
+
items: { href?: string; label: string }[];
|
|
11
|
+
/** aria-label of the <nav> landmark. */
|
|
12
|
+
label?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let { items, label = 'Drobečková navigace' }: Props = $props();
|
|
16
|
+
|
|
17
|
+
// GT Walsheim Medium — Avenir do drobečků nepatří (majitel, 21. 8. 2026):
|
|
18
|
+
// pravidla znají Avenir jen u podnázvu loga.
|
|
19
|
+
const LINK =
|
|
20
|
+
'text-fg-muted1 no-underline transition-[color] duration-[var(--dur-base)] ease-[var(--ease)] hover:text-fg hover:no-underline';
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<!-- overflow-x: long product trails scroll sideways on a phone instead of
|
|
24
|
+
wrapping — kept from the original component, the guide's static pages
|
|
25
|
+
never grow trails that long. -->
|
|
26
|
+
<nav aria-label={label} class="overflow-x-auto font-secondary whitespace-nowrap py-3 [scrollbar-width:none]">
|
|
27
|
+
<ol
|
|
28
|
+
class="flex items-center gap-2 text-step-down2 font-medium tracking-[0.02em] text-fg-muted1"
|
|
29
|
+
>
|
|
30
|
+
{#each items as item, i (i)}
|
|
31
|
+
<li class="flex items-center gap-2">
|
|
32
|
+
{#if i > 0}<span aria-hidden="true" class="text-fg-muted2">/</span>{/if}
|
|
33
|
+
{#if item.href && i < items.length - 1}
|
|
34
|
+
<a href={item.href} class={LINK}>{item.label}</a>
|
|
35
|
+
{:else}
|
|
36
|
+
<span class="text-fg" aria-current="page">{item.label}</span>
|
|
37
|
+
{/if}
|
|
38
|
+
</li>
|
|
39
|
+
{/each}
|
|
40
|
+
</ol>
|
|
41
|
+
</nav>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
items: {
|
|
3
|
+
href?: string;
|
|
4
|
+
label: string;
|
|
5
|
+
}[];
|
|
6
|
+
/** aria-label of the <nav> landmark. */
|
|
7
|
+
label?: string;
|
|
8
|
+
}
|
|
9
|
+
declare const Breadcrumbs: import("svelte").Component<Props, {}, "">;
|
|
10
|
+
type Breadcrumbs = ReturnType<typeof Breadcrumbs>;
|
|
11
|
+
export default Breadcrumbs;
|