@netlify/spark-ui 1.19.0 → 1.20.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/README.md +66 -0
- package/package.json +3 -2
- package/packages/components/ActionArrow.astro +1 -1
- package/packages/components/ActionLink.astro +5 -5
- package/packages/components/Blockquote.astro +22 -22
- package/packages/components/Card.astro +27 -20
- package/packages/components/Eyebrow.astro +4 -4
- package/packages/components/HeadingBlock.astro +12 -12
- package/packages/components/PageHero.astro +4 -4
- package/packages/components/PageHeroGraphic.astro +9 -9
- package/packages/components/PageSection.astro +9 -9
- package/packages/components/Switchback.astro +14 -14
- package/packages/components/SwitchbackGroup.astro +1 -1
- package/packages/components/TabPanelGroup.astro +4 -4
- package/packages/components/TabbedContent.astro +13 -13
- package/packages/components/TabbedContentPanel.astro +14 -14
- package/packages/components/TestimonialSlider.astro +8 -8
- package/packages/components/Ticker.astro +1 -1
- package/packages/components/TrustBarTicker.astro +1 -1
- package/packages/components/TrustBarTickerItem.astro +5 -5
- package/packages/components/preact/AskNetlify/components/ActionBar.tsx +1 -1
- package/packages/styles/base.css +1 -0
- package/packages/styles/layout.compat.css +114 -0
- package/packages/styles/layout.css +35 -35
- package/packages/styles/tokens.compat.css +548 -0
- package/packages/styles/tokens.css +524 -440
- package/packages/styles/tokens.overrides.css +6 -0
- package/skills/landing-page.md +188 -0
|
@@ -0,0 +1,188 @@
|
|
|
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(({ image, ...card }) => (
|
|
84
|
+
<Card {...card}>
|
|
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(({ image, ...item }, i) => (
|
|
96
|
+
<Switchback {...item} isReversed={i % 2 === 0}>
|
|
97
|
+
<Image slot="media" src={image.src} alt={image.alt} width={image.width} height={image.height} />
|
|
98
|
+
</Switchback>
|
|
99
|
+
))}
|
|
100
|
+
</SwitchbackGroup>
|
|
101
|
+
</PageSection>
|
|
102
|
+
|
|
103
|
+
<!-- Tabbed content — isUniformHeight prevents height shift, noPadding for custom layouts -->
|
|
104
|
+
<PageSection>
|
|
105
|
+
<HeadingBlock eyebrow={tabbedContentData.eyebrow} heading={tabbedContentData.heading} />
|
|
106
|
+
<TabbedContent>
|
|
107
|
+
<TabList>
|
|
108
|
+
{tabbedContentData.items.map(({ title, icon, isSelected }) => (
|
|
109
|
+
<Tab isSelected={isSelected}><img src={icon} alt="" width="16" height="16" />{title}</Tab>
|
|
110
|
+
))}
|
|
111
|
+
</TabList>
|
|
112
|
+
<TabPanelGroup isUniformHeight noPadding>
|
|
113
|
+
{tabbedContentData.items.map(({ title, icon, isSelected, ...panel }) => (
|
|
114
|
+
<TabbedContentPanel {...panel} isHidden={!isSelected} />
|
|
115
|
+
))}
|
|
116
|
+
</TabPanelGroup>
|
|
117
|
+
</TabbedContent>
|
|
118
|
+
</PageSection>
|
|
119
|
+
|
|
120
|
+
<!-- Trust bar (logos variant) -->
|
|
121
|
+
<PageSection>
|
|
122
|
+
<HeadingBlock eyebrow={tickerData.eyebrow} heading={tickerData.heading} ctas={tickerData.ctas} alignment="center" />
|
|
123
|
+
<TrustBarTicker>
|
|
124
|
+
{tickerData.logos.map(l => (
|
|
125
|
+
<TrustBarTickerItem><Image src={l.src} alt={l.alt} width={l.width} height={l.height} /></TrustBarTickerItem>
|
|
126
|
+
))}
|
|
127
|
+
</TrustBarTicker>
|
|
128
|
+
</PageSection>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Additional Patterns
|
|
132
|
+
|
|
133
|
+
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):
|
|
134
|
+
```astro
|
|
135
|
+
import FunctionsIcon from '@netlify/spark-ui/assets/icons/functions.svg';
|
|
136
|
+
<Card headline="..." paragraph="..." isCardClickable link={{ id: "link", href: "#" }}>
|
|
137
|
+
<FunctionsIcon slot="icon" />
|
|
138
|
+
</Card>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
HeadingBlock props: `heading`, `headingLevel` (1-4, default 2), `text`, `eyebrow`, `alignment` ("start"|"center"), `headingMaxWidth`, `textMaxWidth`, `ctas`.
|
|
142
|
+
|
|
143
|
+
## Data Shapes
|
|
144
|
+
|
|
145
|
+
Keep content in JSON files. Key shapes:
|
|
146
|
+
|
|
147
|
+
- **Hero:** `{ heading, text, ctas: [{ id, text, href, variant? }] }`
|
|
148
|
+
- **Card item:** `{ eyebrow, headline, paragraph (HTML ok), link: { id, href, text }, image?: { src, alt, width, height }, isCardClickable? }`
|
|
149
|
+
- **Switchback item:** `{ eyebrow, heading, text, list: string[], cta: { id, href, text }, mediaPattern: 1|2|3, image: { src, alt, width, height } }`
|
|
150
|
+
- **TabbedContent item:** `{ title, icon?, isSelected, heading, text?, list?: string[], ctas?, codeBlock?: { heading?, icon?, lang, code } }`
|
|
151
|
+
- **TrustBarTicker:** `{ stats: [{ iconSrc, text }], logos: [{ src, alt, width, height }] }`
|
|
152
|
+
|
|
153
|
+
## Layout Utilities
|
|
154
|
+
|
|
155
|
+
No custom layout CSS. Use these classes:
|
|
156
|
+
|
|
157
|
+
- `.ntl-flow` — Vertical stack. Gap: `--ntl-flow-space`
|
|
158
|
+
- `.ntl-cluster` — Horizontal wrap. Gap: `--ntl-cluster-gap`
|
|
159
|
+
- `.ntl-grid` — Responsive grid. Requires `--ntl-grid-column-min-size`
|
|
160
|
+
- `.ntl-stack` — Overlay children (CSS Grid)
|
|
161
|
+
- `.ntl-breakout` — Full-width breakout
|
|
162
|
+
|
|
163
|
+
## Design Tokens
|
|
164
|
+
|
|
165
|
+
Use `--ntl-` prefixed tokens (auto-flip light/dark):
|
|
166
|
+
|
|
167
|
+
- Backgrounds: `--ntl-color-bg`, `bg-2`, `bg-3`, `bg-inverse`
|
|
168
|
+
- Text: `--ntl-color-text`, `text-weak`, `text-weaker`
|
|
169
|
+
- Headings: `--ntl-heading-text-color`
|
|
170
|
+
- Spacing: `--ntl-space-3xs` to `--ntl-space-4xl`
|
|
171
|
+
- Fonts: `--ntl-font-family-text` (Instrument Sans), `display` (Figtree), `mono` (Martian Mono)
|
|
172
|
+
- Border widths: `--ntl-border-width-thin` (1px), `base` (1.5px), `thick` (2px), `heavy` (3px)
|
|
173
|
+
- Z-index: `--ntl-z-index-base` (0), `raised` (10), `sticky` (100), `overlay` (200), `modal` (300), `toast` (400), `tooltip` (500)
|
|
174
|
+
- Motion durations: `--ntl-motion-duration-fast` (100ms), `base` (200ms), `slow` (300ms), `slower` (500ms)
|
|
175
|
+
- Motion easing: `--ntl-motion-easing-default`, `in`, `out`, `spring` (cubic-bezier overshoot)
|
|
176
|
+
|
|
177
|
+
## Rules
|
|
178
|
+
|
|
179
|
+
1. Always wrap sections in `<PageSection>`.
|
|
180
|
+
2. Use layout utility classes, not custom CSS.
|
|
181
|
+
3. Use `--ntl-` tokens for colors, spacing, typography. No hardcoded values.
|
|
182
|
+
4. Keep content in JSON data files following the shapes above.
|
|
183
|
+
5. Use semantic tokens + `theme` prop for dark mode.
|
|
184
|
+
6. Alternate `isReversed` on Switchbacks.
|
|
185
|
+
7. All images need alt text.
|
|
186
|
+
8. One `h1` only (in PageHero). Use `headingLevel` for h2/h3.
|
|
187
|
+
9. CTAs need `id`, `text`, `href`. Secondary: `variant: "outline"`.
|
|
188
|
+
10. Follow the patterns above. Do not invent custom wrappers.
|