@netlify/spark-ui 1.18.0 → 1.20.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.
@@ -0,0 +1,6 @@
1
+ /* Manual token overrides for values that can't be expressed in DTCG format */
2
+ /* (e.g. CSS keywords like currentcolor, or % units for dimension tokens) */
3
+
4
+ :root {
5
+ --ntl-radius-circle: 50%;
6
+ }
@@ -0,0 +1,189 @@
1
+ # Spark UI Landing Page Guidelines
2
+
3
+ Build a marketing landing page using Netlify Spark UI. Follow these guidelines strictly.
4
+
5
+ ## Page Structure
6
+
7
+ Wrap every section in `<PageSection>` (props: `spaceTop`, `spaceBottom`, `theme`). Use this order (include only what fits the request):
8
+
9
+ 1. **PageHero** or **PageHeroGraphic** — Hero section. Use `PageHeroGraphic` when you need a side-by-side layout with a graphic.
10
+ 2. **TrustBarTicker** — Animated stats or logos
11
+ 3. **Cards grid** — Feature cards in `.ntl-grid`
12
+ 4. **SwitchbackGroup** — Alternating content + media
13
+ 5. **TabbedContent** — Tabbed panels
14
+ 6. **HeadingBlock + CTA** — Closing CTA
15
+
16
+ ## Components
17
+
18
+ | Component | Purpose |
19
+ |-----------|---------|
20
+ | `PageHero` | Centered hero: heading, text, CTAs |
21
+ | `PageHeroGraphic` | Side-by-side hero: left heading + right `graphic` slot |
22
+ | `PageSection` | Section wrapper (spacing, theme) |
23
+ | `HeadingBlock` | Section heading (eyebrow, text, CTAs, alignment) |
24
+ | `Card` | Card with eyebrow, headline, paragraph, image/icon slots, link |
25
+ | `Switchback` / `SwitchbackGroup` | Side-by-side content + media |
26
+ | `TrustBarTicker` / `TrustBarTickerItem` | Animated ticker |
27
+ | `TabbedContent` / `Tab` / `TabList` / `TabPanelGroup` / `TabbedContentPanel` | Tabs |
28
+ | `TestimonialSlider` | Testimonial carousel |
29
+ | `Button` | CTA (variants: primary, outline, ghost) |
30
+ | `ActionLink` | Arrow link |
31
+ | `Eyebrow` | Label text |
32
+ | `CodeBlock` | Code display |
33
+ | `Blockquote` | Quote |
34
+
35
+ All imports: `@netlify/spark-ui/components/{Name}.astro`
36
+
37
+ ## Page Skeleton
38
+
39
+ ```astro
40
+ ---
41
+ import { Image } from 'astro:assets';
42
+ import PageHero from '@netlify/spark-ui/components/PageHero.astro';
43
+ import PageHeroGraphic from '@netlify/spark-ui/components/PageHeroGraphic.astro';
44
+ import PageSection from '@netlify/spark-ui/components/PageSection.astro';
45
+ import HeadingBlock from '@netlify/spark-ui/components/HeadingBlock.astro';
46
+ import Card from '@netlify/spark-ui/components/Card.astro';
47
+ import TrustBarTicker from '@netlify/spark-ui/components/TrustBarTicker.astro';
48
+ import TrustBarTickerItem from '@netlify/spark-ui/components/TrustBarTickerItem.astro';
49
+ import Switchback from '@netlify/spark-ui/components/Switchback.astro';
50
+ import SwitchbackGroup from '@netlify/spark-ui/components/SwitchbackGroup.astro';
51
+ import TabbedContent from '@netlify/spark-ui/components/TabbedContent.astro';
52
+ import Tab from '@netlify/spark-ui/components/Tab.astro';
53
+ import TabList from '@netlify/spark-ui/components/TabList.astro';
54
+ import TabPanelGroup from '@netlify/spark-ui/components/TabPanelGroup.astro';
55
+ import TabbedContentPanel from '@netlify/spark-ui/components/TabbedContentPanel.astro';
56
+
57
+ import heroData from '../data/hero.json';
58
+ import cardData from '../data/cards.json';
59
+ import switchbackData from '../data/switchback.json';
60
+ import tabbedContentData from '../data/tabbed-content.json';
61
+ import tickerData from '../data/trust-bar-ticker.json';
62
+ ---
63
+
64
+ <!-- Option A: Centered hero -->
65
+ <PageHero data-theme="dark" heading={heroData.heading} text={heroData.text} ctas={heroData.ctas} />
66
+
67
+ <!-- Option B: Side-by-side hero with graphic -->
68
+ <PageHeroGraphic data-theme="dark" heading={heroData.heading} text={heroData.text} ctas={heroData.ctas}>
69
+ <Image slot="graphic" src="/hero-graphic.png" alt="..." width={600} height={400} />
70
+ </PageHeroGraphic>
71
+
72
+ <!-- Trust bar (stats variant) -->
73
+ <PageSection spaceTop="var(--ntl-space-l)" spaceBottom="var(--ntl-space-xl)">
74
+ <TrustBarTicker>
75
+ {tickerData.stats.map(s => <TrustBarTickerItem iconSrc={s.iconSrc}>{s.text}</TrustBarTickerItem>)}
76
+ </TrustBarTicker>
77
+ </PageSection>
78
+
79
+ <!-- Cards -->
80
+ <PageSection>
81
+ <HeadingBlock eyebrow={cardData.eyebrow} heading={cardData.heading} text={cardData.text} alignment="center" />
82
+ <div class="ntl-grid" style="--ntl-grid-column-min-size: 20rem;">
83
+ {cardData.items.map(({ eyebrow, heading, text, cta, image }) => (
84
+ <Card eyebrow={eyebrow} headline={heading} paragraph={text} link={cta}>
85
+ {image && <Image slot="image" src={image.src} alt={image.alt} width={image.width} height={image.height} />}
86
+ </Card>
87
+ ))}
88
+ </div>
89
+ </PageSection>
90
+
91
+ <!-- Switchbacks — alternate isReversed, vary mediaPattern 1-3 -->
92
+ <PageSection>
93
+ <HeadingBlock eyebrow={switchbackData.eyebrow} heading={switchbackData.heading} />
94
+ <SwitchbackGroup>
95
+ {switchbackData.items.map(({ eyebrow, heading, text, list, cta, mediaPattern, image }, i) => (
96
+ <Switchback eyebrow={eyebrow} heading={heading} text={text} list={list} cta={cta}
97
+ isReversed={i % 2 === 0} mediaPattern={mediaPattern}>
98
+ <Image slot="media" src={image.src} alt={image.alt} width={image.width} height={image.height} />
99
+ </Switchback>
100
+ ))}
101
+ </SwitchbackGroup>
102
+ </PageSection>
103
+
104
+ <!-- Tabbed content — isUniformHeight prevents height shift, noPadding for custom layouts -->
105
+ <PageSection>
106
+ <HeadingBlock eyebrow={tabbedContentData.eyebrow} heading={tabbedContentData.heading} />
107
+ <TabbedContent>
108
+ <TabList>
109
+ {tabbedContentData.items.map(({ title, icon, isSelected }) => (
110
+ <Tab isSelected={isSelected}><img src={icon} alt="" width="16" height="16" />{title}</Tab>
111
+ ))}
112
+ </TabList>
113
+ <TabPanelGroup isUniformHeight noPadding>
114
+ {tabbedContentData.items.map(({ heading, list, text, ctas, codeBlock, isSelected }) => (
115
+ <TabbedContentPanel isHidden={!isSelected} heading={heading} list={list} text={text} ctas={ctas} codeBlock={codeBlock} />
116
+ ))}
117
+ </TabPanelGroup>
118
+ </TabbedContent>
119
+ </PageSection>
120
+
121
+ <!-- Trust bar (logos variant) -->
122
+ <PageSection>
123
+ <HeadingBlock eyebrow={tickerData.eyebrow} heading={tickerData.heading} ctas={tickerData.ctas} alignment="center" />
124
+ <TrustBarTicker>
125
+ {tickerData.logos.map(l => (
126
+ <TrustBarTickerItem><Image src={l.src} alt={l.alt} width={l.width} height={l.height} /></TrustBarTickerItem>
127
+ ))}
128
+ </TrustBarTicker>
129
+ </PageSection>
130
+ ```
131
+
132
+ ## Additional Patterns
133
+
134
+ Card with icon (bundled SVGs at `@netlify/spark-ui/assets/icons/*.svg`: ai, cdn, data-storage, dev, ecommerce, edge-functions, forms, functions, marketing, security, sparkles):
135
+ ```astro
136
+ import FunctionsIcon from '@netlify/spark-ui/assets/icons/functions.svg';
137
+ <Card headline="..." paragraph="..." isCardClickable link={{ id: "link", href: "#" }}>
138
+ <FunctionsIcon slot="icon" />
139
+ </Card>
140
+ ```
141
+
142
+ HeadingBlock props: `heading`, `headingLevel` (1-4, default 2), `text`, `eyebrow`, `alignment` ("start"|"center"), `headingMaxWidth`, `textMaxWidth`, `ctas`.
143
+
144
+ ## Data Shapes
145
+
146
+ Keep content in JSON files. Key shapes:
147
+
148
+ - **Hero:** `{ heading, text, ctas: [{ id, text, href, variant? }] }`
149
+ - **Card item:** `{ eyebrow, heading, text (HTML ok), cta: { id, href, text }, image?: { src, alt, width, height } }`
150
+ - **Switchback item:** `{ eyebrow, heading, text, list: string[], cta: { id, href, text }, mediaPattern: 1|2|3, image: { src, alt, width, height } }`
151
+ - **TabbedContent item:** `{ title, icon?, isSelected, heading, text?, list?: string[], ctas?, codeBlock?: { heading?, icon?, lang, code } }`
152
+ - **TrustBarTicker:** `{ stats: [{ iconSrc, text }], logos: [{ src, alt, width, height }] }`
153
+
154
+ ## Layout Utilities
155
+
156
+ No custom layout CSS. Use these classes:
157
+
158
+ - `.ntl-flow` — Vertical stack. Gap: `--ntl-flow-space`
159
+ - `.ntl-cluster` — Horizontal wrap. Gap: `--ntl-cluster-gap`
160
+ - `.ntl-grid` — Responsive grid. Requires `--ntl-grid-column-min-size`
161
+ - `.ntl-stack` — Overlay children (CSS Grid)
162
+ - `.ntl-breakout` — Full-width breakout
163
+
164
+ ## Design Tokens
165
+
166
+ Use `--ntl-` prefixed tokens (auto-flip light/dark):
167
+
168
+ - Backgrounds: `--ntl-color-bg`, `bg-2`, `bg-3`, `bg-inverse`
169
+ - Text: `--ntl-color-text`, `text-weak`, `text-weaker`
170
+ - Headings: `--ntl-heading-text-color`
171
+ - Spacing: `--ntl-space-3xs` to `--ntl-space-4xl`
172
+ - Fonts: `--ntl-font-family-text` (Instrument Sans), `display` (Figtree), `mono` (Martian Mono)
173
+ - Border widths: `--ntl-border-width-thin` (1px), `base` (1.5px), `thick` (2px), `heavy` (3px)
174
+ - Z-index: `--ntl-z-index-base` (0), `raised` (10), `sticky` (100), `overlay` (200), `modal` (300), `toast` (400), `tooltip` (500)
175
+ - Motion durations: `--ntl-motion-duration-fast` (100ms), `base` (200ms), `slow` (300ms), `slower` (500ms)
176
+ - Motion easing: `--ntl-motion-easing-default`, `in`, `out`, `spring` (cubic-bezier overshoot)
177
+
178
+ ## Rules
179
+
180
+ 1. Always wrap sections in `<PageSection>`.
181
+ 2. Use layout utility classes, not custom CSS.
182
+ 3. Use `--ntl-` tokens for colors, spacing, typography. No hardcoded values.
183
+ 4. Keep content in JSON data files following the shapes above.
184
+ 5. Use semantic tokens + `theme` prop for dark mode.
185
+ 6. Alternate `isReversed` on Switchbacks.
186
+ 7. All images need alt text.
187
+ 8. One `h1` only (in PageHero). Use `headingLevel` for h2/h3.
188
+ 9. CTAs need `id`, `text`, `href`. Secondary: `variant: "outline"`.
189
+ 10. Follow the patterns above. Do not invent custom wrappers.