@invisibleloop/pulse 0.12.2 → 0.14.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/.claude/commands/verify.md +9 -5
- package/.claude/pulse-checklist.md +21 -1
- package/.github/workflows/publish.yml +1 -0
- package/.pulse-verified +1 -0
- package/CLAUDE.md +56 -15
- package/docs/.claude/commands/verify.md +4 -0
- package/docs/.claude/pulse-checklist.md +36 -2
- package/docs/public/.pulse-ui-version +1 -1
- package/docs/public/andypixel.css +237 -0
- package/docs/public/bookworms.css +422 -0
- package/docs/public/coutumes.css +584 -0
- package/docs/public/images/bookworms/hero-books.jpg +0 -0
- package/docs/public/images/bookworms/how-we-read-now.jpg +0 -0
- package/docs/public/images/bookworms/lantern-keepers.jpg +0 -0
- package/docs/public/images/bookworms/provincial-bookseller.jpg +0 -0
- package/docs/public/images/journey/178.jpg +0 -0
- package/docs/public/images/journey/29.jpg +0 -0
- package/docs/public/images/retrowheels/hero.jpg +0 -0
- package/docs/public/images/shop/devbook-pro.jpg +0 -0
- package/docs/public/images/suzukibento/1015.jpg +0 -0
- package/docs/public/images/suzukibento/1025.jpg +0 -0
- package/docs/public/images/suzukibento/1043.jpg +0 -0
- package/docs/public/images/suzukibento/1059.jpg +0 -0
- package/docs/public/images/suzukibento/1062.jpg +0 -0
- package/docs/public/images/suzukibento/1084.jpg +0 -0
- package/docs/public/images/swish/1469334031218-e382a71b716b.jpg +0 -0
- package/docs/public/images/swish/1483985988355-763728e1935b.jpg +0 -0
- package/docs/public/images/swish/1485518882345-15568b007407.jpg +0 -0
- package/docs/public/images/swish/1490481651871-ab68de25d43d.jpg +0 -0
- package/docs/public/images/swish/1496747611176-843222e1e57c.jpg +0 -0
- package/docs/public/images/swish/1502716119720-b23a93e5fe1b.jpg +0 -0
- package/docs/public/images/swish/1539109136881-3be0616acf4b.jpg +0 -0
- package/docs/public/images/swish/1551803091-e20673f15770.jpg +0 -0
- package/docs/public/images/swish/1558769132-cb1aea458c5e.jpg +0 -0
- package/docs/public/images/swish/1572804013309-59a88b7e92f1.jpg +0 -0
- package/docs/public/journey.css +528 -0
- package/docs/public/pulse-ui.css +1051 -25
- package/docs/public/pulse-ui.js +6 -0
- package/docs/public/retrowheels.css +402 -0
- package/docs/public/shop.css +235 -0
- package/docs/public/sparkys.css +349 -0
- package/docs/public/suzukibento.css +517 -0
- package/docs/public/swish.css +394 -0
- package/docs/server.js +19 -4
- package/docs/src/lib/nav.js +14 -3
- package/docs/src/pages/components/decorate.js +74 -0
- package/docs/src/pages/components/gallery.js +102 -0
- package/docs/src/pages/components/hero.js +1 -0
- package/docs/src/pages/components/marquee.js +76 -0
- package/docs/src/pages/components/phone-frame.js +26 -7
- package/docs/src/pages/components/photo-card.js +74 -0
- package/docs/src/pages/components/section.js +40 -1
- package/docs/src/pages/meta.js +26 -0
- package/docs/src/pages/templates/andypixel.js +260 -0
- package/docs/src/pages/templates/bookworms.js +227 -0
- package/docs/src/pages/templates/coutumes.js +226 -0
- package/docs/src/pages/templates/journey.js +186 -0
- package/docs/src/pages/templates/retrowheels.js +342 -0
- package/docs/src/pages/templates/shop.js +257 -0
- package/docs/src/pages/templates/sparkys.js +249 -0
- package/docs/src/pages/templates/suzukibento.js +200 -0
- package/docs/src/pages/templates/swish.js +313 -0
- package/docs/src/pages/templates.js +313 -2
- package/docs/src/pages/testing.js +2 -1
- package/examples/andypixel.css +237 -0
- package/examples/andypixel.js +258 -0
- package/examples/bookworms.css +422 -0
- package/examples/bookworms.js +225 -0
- package/examples/contact.js +1 -0
- package/examples/counter.js +1 -0
- package/examples/dev.server.js +17 -0
- package/examples/journey.css +528 -0
- package/examples/journey.js +186 -0
- package/examples/pricing.js +1 -0
- package/examples/quiz.js +1 -0
- package/examples/retrowheels.css +402 -0
- package/examples/retrowheels.js +340 -0
- package/examples/shared.js +6 -0
- package/examples/suzukibento.css +513 -0
- package/examples/suzukibento.js +200 -0
- package/examples/swish.css +394 -0
- package/examples/swish.js +311 -0
- package/examples/todos.js +1 -0
- package/package.json +1 -1
- package/public/.pulse-ui-version +1 -1
- package/public/images/bookworms/hero-books.jpg +0 -0
- package/public/images/bookworms/how-we-read-now.jpg +0 -0
- package/public/images/bookworms/lantern-keepers.jpg +0 -0
- package/public/images/bookworms/provincial-bookseller.jpg +0 -0
- package/public/images/journey/178.jpg +0 -0
- package/public/images/journey/29.jpg +0 -0
- package/public/images/retrowheels/hero.jpg +0 -0
- package/public/images/shop/devbook-pro.jpg +0 -0
- package/public/images/suzukibento/1015.jpg +0 -0
- package/public/images/suzukibento/1025.jpg +0 -0
- package/public/images/suzukibento/1043.jpg +0 -0
- package/public/images/suzukibento/1059.jpg +0 -0
- package/public/images/suzukibento/1062.jpg +0 -0
- package/public/images/suzukibento/1084.jpg +0 -0
- package/public/images/swish/1469334031218-e382a71b716b.jpg +0 -0
- package/public/images/swish/1483985988355-763728e1935b.jpg +0 -0
- package/public/images/swish/1485518882345-15568b007407.jpg +0 -0
- package/public/images/swish/1490481651871-ab68de25d43d.jpg +0 -0
- package/public/images/swish/1496747611176-843222e1e57c.jpg +0 -0
- package/public/images/swish/1502716119720-b23a93e5fe1b.jpg +0 -0
- package/public/images/swish/1539109136881-3be0616acf4b.jpg +0 -0
- package/public/images/swish/1551803091-e20673f15770.jpg +0 -0
- package/public/images/swish/1558769132-cb1aea458c5e.jpg +0 -0
- package/public/images/swish/1572804013309-59a88b7e92f1.jpg +0 -0
- package/public/pulse-ui.css +1051 -22
- package/public/pulse-ui.js +6 -0
- package/scripts/build.js +72 -18
- package/src/agent/checklist.md +20 -2
- package/src/agent/commands/verify.md +4 -0
- package/src/agent/guide-components.md +146 -8
- package/src/agent/guide-design-gallery.md +288 -0
- package/src/agent/guide-design-references.md +172 -0
- package/src/agent/guide-explore.md +313 -0
- package/src/agent/guide-styles.md +197 -11
- package/src/agent/guide-templates.md +220 -10
- package/src/agent/guide.md +9 -4
- package/src/agent/workflow.md +208 -58
- package/src/cli/dev.js +39 -7
- package/src/cli/fmt.js +113 -0
- package/src/cli/index.js +63 -30
- package/src/cli/scaffold.js +2 -2
- package/src/html.js +6 -1
- package/src/mcp/server.js +2415 -101
- package/src/mcp/validate-worker.js +34 -5
- package/src/runtime/ssr.js +4 -1
- package/src/server/index.js +22 -2
- package/src/spec/schema.js +5 -0
- package/src/testing/html.js +130 -4
- package/src/testing/testing.test.js +58 -0
- package/src/ui/banner.js +24 -9
- package/src/ui/brand-statement.js +58 -0
- package/src/ui/button.js +12 -1
- package/src/ui/card.js +20 -1
- package/src/ui/colophon.js +29 -0
- package/src/ui/contact-row.js +41 -0
- package/src/ui/cta.js +18 -4
- package/src/ui/decorate.js +55 -0
- package/src/ui/display-heading.js +44 -0
- package/src/ui/drop-cap.js +29 -0
- package/src/ui/editorial-hero.js +63 -0
- package/src/ui/entry-list.js +36 -0
- package/src/ui/feature.js +20 -3
- package/src/ui/footer.js +78 -9
- package/src/ui/gallery.js +56 -0
- package/src/ui/hero.js +68 -18
- package/src/ui/image-pair.js +72 -0
- package/src/ui/index.js +20 -0
- package/src/ui/input.js +1 -0
- package/src/ui/marquee.js +48 -0
- package/src/ui/nav.js +19 -9
- package/src/ui/phone-frame.js +6 -5
- package/src/ui/photo-card.js +44 -0
- package/src/ui/product-card.js +65 -0
- package/src/ui/pullquote.js +14 -3
- package/src/ui/search.js +1 -0
- package/src/ui/section-label.js +38 -0
- package/src/ui/section.js +1 -1
- package/src/ui/service-card.js +47 -0
- package/src/ui/stat-grid.js +40 -0
- package/src/ui/stat.js +16 -2
- package/src/ui/testimonial.js +17 -6
- package/src/ui/textarea.js +1 -0
- package/src/ui/top-strip.js +47 -0
- package/src/ui/trust-list.js +46 -0
- package/src/ui/ui.test.js +35 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* suzukibento.css — Susuki Bento artist portfolio example
|
|
3
|
+
*
|
|
4
|
+
* Brutalist gallery palette: near-black ground, bone-white text, vermilion accent.
|
|
5
|
+
* Dark by default — palette set as bare token overrides on :root so they
|
|
6
|
+
* cascade into --ui-bg, --ui-text, --ui-accent etc. via pulse-ui.css.
|
|
7
|
+
* Zero border-radius throughout (--radius: 0px).
|
|
8
|
+
* Typography: Inter → system-ui (no external font requests).
|
|
9
|
+
*
|
|
10
|
+
* Token responsibility:
|
|
11
|
+
* :root — dark palette + zero-radius + font stack
|
|
12
|
+
* Component styles — layout only, var() references throughout
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/* ==========================================================================
|
|
16
|
+
1. Token overrides (hex values live here)
|
|
17
|
+
========================================================================== */
|
|
18
|
+
|
|
19
|
+
/*
|
|
20
|
+
* Override the bare cascade tokens that --ui-* variables reference.
|
|
21
|
+
* See pulse-ui.css :root: --ui-bg: var(--bg, ...) etc.
|
|
22
|
+
* Setting --bg, --surface, --text, --accent etc. on :root is enough to
|
|
23
|
+
* retheme the entire UI without touching --ui-* directly.
|
|
24
|
+
*/
|
|
25
|
+
:root {
|
|
26
|
+
--bg: #0a0a0a;
|
|
27
|
+
--surface: #131313;
|
|
28
|
+
--surface-2: #1c1c1c;
|
|
29
|
+
--border: #2a2a2a;
|
|
30
|
+
--text: #f5f3ef;
|
|
31
|
+
--muted: #8a8780;
|
|
32
|
+
--accent: #ff4a2a; /* vermilion — 4.5:1 on #0a0a0a */
|
|
33
|
+
--accent-hover: #ff6b4f;
|
|
34
|
+
--accent-dim: rgba(255, 74, 42, 0.14);
|
|
35
|
+
--accent-text: #0a0a0a;
|
|
36
|
+
|
|
37
|
+
/* Zero-radius brutalist design language */
|
|
38
|
+
--radius: 0px;
|
|
39
|
+
--radius-sm: 0px;
|
|
40
|
+
|
|
41
|
+
/* Font stack — system Inter, no HTTP request */
|
|
42
|
+
--font: 'Inter', system-ui, sans-serif;
|
|
43
|
+
--font-display: 'Inter', system-ui, sans-serif;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* ==========================================================================
|
|
47
|
+
2. Component styles (var() only — no hex values below this line)
|
|
48
|
+
========================================================================== */
|
|
49
|
+
|
|
50
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
51
|
+
|
|
52
|
+
body {
|
|
53
|
+
font-family: var(--ui-font, system-ui, sans-serif);
|
|
54
|
+
line-height: 1.5;
|
|
55
|
+
-webkit-font-smoothing: antialiased;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* --------------------------------------------------------------------------
|
|
59
|
+
Skip link
|
|
60
|
+
-------------------------------------------------------------------------- */
|
|
61
|
+
.skip-link {
|
|
62
|
+
position: absolute;
|
|
63
|
+
left: -9999px;
|
|
64
|
+
top: 0;
|
|
65
|
+
background: var(--ui-accent);
|
|
66
|
+
color: var(--ui-accent-text);
|
|
67
|
+
padding: var(--ui-space-2) var(--ui-space-4);
|
|
68
|
+
z-index: 100;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.skip-link:focus { left: var(--ui-space-2); }
|
|
72
|
+
|
|
73
|
+
/* --------------------------------------------------------------------------
|
|
74
|
+
Sticky top nav (hand-rolled — no nav() component)
|
|
75
|
+
-------------------------------------------------------------------------- */
|
|
76
|
+
.sb-nav {
|
|
77
|
+
position: sticky;
|
|
78
|
+
top: 0;
|
|
79
|
+
z-index: 50;
|
|
80
|
+
display: flex;
|
|
81
|
+
justify-content: space-between;
|
|
82
|
+
align-items: center;
|
|
83
|
+
padding: var(--ui-space-5) var(--ui-space-8);
|
|
84
|
+
background: var(--ui-bg);
|
|
85
|
+
border-bottom: 1px solid var(--ui-border);
|
|
86
|
+
font-size: var(--ui-text-sm);
|
|
87
|
+
text-transform: uppercase;
|
|
88
|
+
letter-spacing: 0.08em;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.sb-nav__brand {
|
|
92
|
+
font-weight: 700;
|
|
93
|
+
color: var(--ui-text);
|
|
94
|
+
text-decoration: none;
|
|
95
|
+
display: inline-flex;
|
|
96
|
+
align-items: center;
|
|
97
|
+
gap: 0.3em;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.sb-nav__dot {
|
|
101
|
+
display: inline-block;
|
|
102
|
+
width: 0.45em;
|
|
103
|
+
height: 0.45em;
|
|
104
|
+
border-radius: 50%;
|
|
105
|
+
background: var(--ui-accent);
|
|
106
|
+
flex-shrink: 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.sb-nav__links {
|
|
110
|
+
display: flex;
|
|
111
|
+
gap: var(--ui-space-6);
|
|
112
|
+
list-style: none;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.sb-nav__links a {
|
|
116
|
+
color: var(--ui-muted);
|
|
117
|
+
text-decoration: none;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.sb-nav__links a:hover,
|
|
121
|
+
.sb-nav__links a:focus { color: var(--ui-accent); }
|
|
122
|
+
|
|
123
|
+
/* Burger — hidden on desktop */
|
|
124
|
+
.sb-nav__toggle-input { display: none; }
|
|
125
|
+
.sb-nav__burger { display: none; }
|
|
126
|
+
|
|
127
|
+
@media (max-width: 600px) {
|
|
128
|
+
.sb-nav {
|
|
129
|
+
flex-wrap: wrap;
|
|
130
|
+
padding: var(--ui-space-4) var(--ui-space-5);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.sb-nav__burger {
|
|
134
|
+
display: flex;
|
|
135
|
+
flex-direction: column;
|
|
136
|
+
justify-content: space-between;
|
|
137
|
+
width: 22px;
|
|
138
|
+
height: 16px;
|
|
139
|
+
cursor: pointer;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.sb-nav__burger span {
|
|
143
|
+
display: block;
|
|
144
|
+
height: 2px;
|
|
145
|
+
background: var(--ui-text);
|
|
146
|
+
transition: transform 0.2s, opacity 0.2s;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.sb-nav__toggle-input:checked ~ .sb-nav__burger span:nth-child(1) {
|
|
150
|
+
transform: translateY(7px) rotate(45deg);
|
|
151
|
+
}
|
|
152
|
+
.sb-nav__toggle-input:checked ~ .sb-nav__burger span:nth-child(2) {
|
|
153
|
+
opacity: 0;
|
|
154
|
+
}
|
|
155
|
+
.sb-nav__toggle-input:checked ~ .sb-nav__burger span:nth-child(3) {
|
|
156
|
+
transform: translateY(-7px) rotate(-45deg);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.sb-nav nav {
|
|
160
|
+
display: none;
|
|
161
|
+
position: absolute;
|
|
162
|
+
top: 100%;
|
|
163
|
+
left: 0;
|
|
164
|
+
right: 0;
|
|
165
|
+
background: var(--ui-bg);
|
|
166
|
+
border-bottom: 1px solid var(--ui-border);
|
|
167
|
+
z-index: 49;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.sb-nav__toggle-input:checked ~ nav {
|
|
171
|
+
display: block;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
.sb-nav__links {
|
|
175
|
+
flex-direction: column;
|
|
176
|
+
gap: 0;
|
|
177
|
+
padding: var(--ui-space-4) var(--ui-space-5);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.sb-nav__links li { border-bottom: 1px solid var(--ui-border); }
|
|
181
|
+
|
|
182
|
+
.sb-nav__links a {
|
|
183
|
+
display: block;
|
|
184
|
+
padding: var(--ui-space-3) 0;
|
|
185
|
+
color: var(--ui-text);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* --------------------------------------------------------------------------
|
|
190
|
+
Hero
|
|
191
|
+
-------------------------------------------------------------------------- */
|
|
192
|
+
.sb-hero {
|
|
193
|
+
padding: var(--ui-space-24) var(--ui-space-8) var(--ui-space-16);
|
|
194
|
+
border-bottom: 1px solid var(--ui-border);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.sb-hero__eyebrow {
|
|
198
|
+
display: inline-block;
|
|
199
|
+
font-size: var(--ui-text-sm);
|
|
200
|
+
text-transform: uppercase;
|
|
201
|
+
letter-spacing: 0.18em;
|
|
202
|
+
color: var(--ui-accent);
|
|
203
|
+
margin-bottom: var(--ui-space-6);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.sb-hero__title {
|
|
207
|
+
font-size: clamp(3.5rem, 12vw, 11rem);
|
|
208
|
+
font-weight: 800;
|
|
209
|
+
line-height: 0.9;
|
|
210
|
+
letter-spacing: -0.05em;
|
|
211
|
+
color: var(--ui-text);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.sb-hero__title em {
|
|
215
|
+
font-style: normal;
|
|
216
|
+
color: var(--ui-accent);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.sb-hero__meta {
|
|
220
|
+
margin-top: var(--ui-space-10);
|
|
221
|
+
display: flex;
|
|
222
|
+
flex-wrap: wrap;
|
|
223
|
+
gap: var(--ui-space-8);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.sb-hero__meta-label {
|
|
227
|
+
display: block;
|
|
228
|
+
font-size: var(--ui-text-sm);
|
|
229
|
+
text-transform: uppercase;
|
|
230
|
+
letter-spacing: 0.12em;
|
|
231
|
+
color: var(--ui-muted);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.sb-hero__meta strong {
|
|
235
|
+
display: block;
|
|
236
|
+
color: var(--ui-text);
|
|
237
|
+
font-weight: 600;
|
|
238
|
+
margin-top: var(--ui-space-1);
|
|
239
|
+
letter-spacing: 0.04em;
|
|
240
|
+
font-size: var(--ui-text-sm);
|
|
241
|
+
text-transform: uppercase;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* --------------------------------------------------------------------------
|
|
245
|
+
Section scaffolding
|
|
246
|
+
-------------------------------------------------------------------------- */
|
|
247
|
+
.sb-section {
|
|
248
|
+
padding: var(--ui-space-20) var(--ui-space-8);
|
|
249
|
+
border-bottom: 1px solid var(--ui-border);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.sb-section__head {
|
|
253
|
+
display: flex;
|
|
254
|
+
justify-content: space-between;
|
|
255
|
+
align-items: baseline;
|
|
256
|
+
margin-bottom: var(--ui-space-12);
|
|
257
|
+
gap: var(--ui-space-6);
|
|
258
|
+
flex-wrap: wrap;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.sb-section__label {
|
|
262
|
+
font-size: var(--ui-text-sm);
|
|
263
|
+
text-transform: uppercase;
|
|
264
|
+
letter-spacing: 0.18em;
|
|
265
|
+
color: var(--ui-accent);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.sb-section__title {
|
|
269
|
+
font-size: clamp(1.75rem, 4vw, 2.5rem);
|
|
270
|
+
font-weight: 700;
|
|
271
|
+
letter-spacing: -0.02em;
|
|
272
|
+
color: var(--ui-text);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* --------------------------------------------------------------------------
|
|
276
|
+
Works grid (12-col asymmetric layout)
|
|
277
|
+
-------------------------------------------------------------------------- */
|
|
278
|
+
.sb-works {
|
|
279
|
+
display: grid;
|
|
280
|
+
grid-template-columns: repeat(12, 1fr);
|
|
281
|
+
grid-auto-rows: 120px;
|
|
282
|
+
gap: var(--ui-space-4);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.sb-work {
|
|
286
|
+
position: relative;
|
|
287
|
+
overflow: hidden;
|
|
288
|
+
background: var(--ui-surface);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.sb-work img {
|
|
292
|
+
width: 100%;
|
|
293
|
+
height: 100%;
|
|
294
|
+
object-fit: cover;
|
|
295
|
+
display: block;
|
|
296
|
+
filter: grayscale(15%) contrast(1.05);
|
|
297
|
+
transition: filter 0.4s ease, transform 0.6s ease;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.sb-work:hover img {
|
|
301
|
+
filter: none;
|
|
302
|
+
transform: scale(1.02);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.sb-work__caption {
|
|
306
|
+
position: absolute;
|
|
307
|
+
left: var(--ui-space-3);
|
|
308
|
+
bottom: var(--ui-space-3);
|
|
309
|
+
background: var(--ui-bg);
|
|
310
|
+
color: var(--ui-text);
|
|
311
|
+
padding: var(--ui-space-1) var(--ui-space-3);
|
|
312
|
+
font-size: var(--ui-text-xs);
|
|
313
|
+
text-transform: uppercase;
|
|
314
|
+
letter-spacing: 0.1em;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
.sb-work__caption span {
|
|
318
|
+
color: var(--ui-muted);
|
|
319
|
+
margin-left: var(--ui-space-2);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/* Explicit positions — gap-free brickwork layout */
|
|
323
|
+
.sb-work--a { grid-column: 1 / 8; grid-row: 1 / 5; }
|
|
324
|
+
.sb-work--b { grid-column: 8 / 13; grid-row: 1 / 4; }
|
|
325
|
+
.sb-work--c { grid-column: 8 / 13; grid-row: 4 / 8; }
|
|
326
|
+
.sb-work--d { grid-column: 1 / 5; grid-row: 5 / 8; }
|
|
327
|
+
.sb-work--e { grid-column: 5 / 8; grid-row: 5 / 8; }
|
|
328
|
+
.sb-work--f { grid-column: 1 / 13; grid-row: 8 / 12; }
|
|
329
|
+
|
|
330
|
+
@media (max-width: 720px) {
|
|
331
|
+
.sb-works { grid-auto-rows: 80px; }
|
|
332
|
+
.sb-work--a, .sb-work--b, .sb-work--c,
|
|
333
|
+
.sb-work--d, .sb-work--e, .sb-work--f {
|
|
334
|
+
grid-column: span 12;
|
|
335
|
+
grid-row: span 3;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* --------------------------------------------------------------------------
|
|
340
|
+
Artist statement
|
|
341
|
+
-------------------------------------------------------------------------- */
|
|
342
|
+
.sb-statement {
|
|
343
|
+
max-width: 56rem;
|
|
344
|
+
display: grid;
|
|
345
|
+
grid-template-columns: 1fr 2fr;
|
|
346
|
+
gap: var(--ui-space-12);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.sb-statement p {
|
|
350
|
+
font-size: var(--ui-text-xl);
|
|
351
|
+
line-height: 1.6;
|
|
352
|
+
color: var(--ui-text);
|
|
353
|
+
margin-bottom: var(--ui-space-5);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.sb-statement p + p {
|
|
357
|
+
color: var(--ui-muted);
|
|
358
|
+
font-size: var(--ui-text-lg);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.sb-statement__bio {
|
|
362
|
+
font-size: var(--ui-text-sm);
|
|
363
|
+
color: var(--ui-muted);
|
|
364
|
+
line-height: 1.7;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
.sb-statement__bio dt {
|
|
368
|
+
text-transform: uppercase;
|
|
369
|
+
letter-spacing: 0.12em;
|
|
370
|
+
color: var(--ui-accent);
|
|
371
|
+
font-size: var(--ui-text-xs);
|
|
372
|
+
margin-top: var(--ui-space-4);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.sb-statement__bio dd { color: var(--ui-text); }
|
|
376
|
+
|
|
377
|
+
@media (max-width: 720px) {
|
|
378
|
+
.sb-statement { grid-template-columns: 1fr; }
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/* --------------------------------------------------------------------------
|
|
382
|
+
Exhibition table
|
|
383
|
+
-------------------------------------------------------------------------- */
|
|
384
|
+
.sb-exhibitions {
|
|
385
|
+
list-style: none;
|
|
386
|
+
border-top: 1px solid var(--ui-border);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
.sb-exhibition {
|
|
390
|
+
display: grid;
|
|
391
|
+
grid-template-columns: 80px 1fr 1fr 120px;
|
|
392
|
+
gap: var(--ui-space-6);
|
|
393
|
+
padding: var(--ui-space-5) 0;
|
|
394
|
+
border-bottom: 1px solid var(--ui-border);
|
|
395
|
+
align-items: baseline;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.sb-exhibition__year {
|
|
399
|
+
font-weight: 700;
|
|
400
|
+
color: var(--ui-accent);
|
|
401
|
+
font-variant-numeric: tabular-nums;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.sb-exhibition__title { color: var(--ui-text); font-weight: 500; }
|
|
405
|
+
.sb-exhibition__venue { color: var(--ui-muted); }
|
|
406
|
+
|
|
407
|
+
.sb-exhibition__tag {
|
|
408
|
+
font-size: var(--ui-text-xs);
|
|
409
|
+
text-transform: uppercase;
|
|
410
|
+
letter-spacing: 0.1em;
|
|
411
|
+
color: var(--ui-muted);
|
|
412
|
+
text-align: right;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.sb-exhibition__tag--solo { color: var(--ui-accent); }
|
|
416
|
+
|
|
417
|
+
@media (max-width: 720px) {
|
|
418
|
+
.sb-exhibition { grid-template-columns: 60px 1fr; }
|
|
419
|
+
.sb-exhibition__venue,
|
|
420
|
+
.sb-exhibition__tag { grid-column: 2; text-align: left; }
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/* --------------------------------------------------------------------------
|
|
424
|
+
Press list with dot separator
|
|
425
|
+
-------------------------------------------------------------------------- */
|
|
426
|
+
.sb-press {
|
|
427
|
+
display: flex;
|
|
428
|
+
flex-wrap: wrap;
|
|
429
|
+
gap: var(--ui-space-8);
|
|
430
|
+
font-size: var(--ui-text-lg);
|
|
431
|
+
font-weight: 500;
|
|
432
|
+
color: var(--ui-muted);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
.sb-press span { white-space: nowrap; }
|
|
436
|
+
|
|
437
|
+
.sb-press span + span::before {
|
|
438
|
+
content: "\00b7";
|
|
439
|
+
margin-right: var(--ui-space-8);
|
|
440
|
+
color: var(--ui-accent);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
@media (max-width: 600px) {
|
|
444
|
+
.sb-press {
|
|
445
|
+
flex-direction: column;
|
|
446
|
+
gap: var(--ui-space-3);
|
|
447
|
+
font-size: var(--ui-text-base);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
.sb-press span + span::before { display: none; }
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/* --------------------------------------------------------------------------
|
|
454
|
+
Contact
|
|
455
|
+
-------------------------------------------------------------------------- */
|
|
456
|
+
.sb-contact {
|
|
457
|
+
display: grid;
|
|
458
|
+
grid-template-columns: 1fr 1fr;
|
|
459
|
+
gap: var(--ui-space-12);
|
|
460
|
+
align-items: start;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.sb-contact h3 {
|
|
464
|
+
font-size: var(--ui-text-sm);
|
|
465
|
+
text-transform: uppercase;
|
|
466
|
+
letter-spacing: 0.18em;
|
|
467
|
+
color: var(--ui-accent);
|
|
468
|
+
margin-bottom: var(--ui-space-4);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
.sb-contact p,
|
|
472
|
+
.sb-contact a {
|
|
473
|
+
font-size: var(--ui-text-lg);
|
|
474
|
+
color: var(--ui-text);
|
|
475
|
+
line-height: 1.6;
|
|
476
|
+
text-decoration: none;
|
|
477
|
+
display: block;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.sb-contact a:hover { color: var(--ui-accent); }
|
|
481
|
+
|
|
482
|
+
@media (max-width: 720px) {
|
|
483
|
+
.sb-contact { grid-template-columns: 1fr; gap: var(--ui-space-8); }
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* --------------------------------------------------------------------------
|
|
487
|
+
Footer
|
|
488
|
+
-------------------------------------------------------------------------- */
|
|
489
|
+
.sb-foot {
|
|
490
|
+
display: flex;
|
|
491
|
+
justify-content: space-between;
|
|
492
|
+
padding: var(--ui-space-6) var(--ui-space-8);
|
|
493
|
+
font-size: var(--ui-text-xs);
|
|
494
|
+
text-transform: uppercase;
|
|
495
|
+
letter-spacing: 0.12em;
|
|
496
|
+
color: var(--ui-muted);
|
|
497
|
+
border-top: 1px solid var(--ui-border);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
@media (max-width: 600px) {
|
|
501
|
+
.sb-foot {
|
|
502
|
+
flex-direction: column;
|
|
503
|
+
gap: var(--ui-space-2);
|
|
504
|
+
padding: var(--ui-space-5);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.sb-foot__link {
|
|
509
|
+
color: var(--ui-muted);
|
|
510
|
+
text-decoration: none;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.sb-foot__link:hover { color: var(--ui-text); }
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulse — Susuki Bento artist portfolio example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates:
|
|
5
|
+
* - Dark-by-default brutalist gallery palette (near-black, bone, vermilion)
|
|
6
|
+
* - Zero-radius design language (--radius: 0px overrides Pulse defaults)
|
|
7
|
+
* - Asymmetric CSS grid works gallery (12-col with mixed span sizes)
|
|
8
|
+
* - Pure vanilla HTML throughout — no Pulse UI components used.
|
|
9
|
+
* Shows that Pulse views are just template literals; you can write
|
|
10
|
+
* raw HTML for fully custom layouts without any component overhead.
|
|
11
|
+
* - Custom sticky nav and simple footer hand-rolled in the view
|
|
12
|
+
* - HTML-escaped data with a utility function (safe pattern for user data)
|
|
13
|
+
* - Hover effect with CSS filter: grayscale + scale on artwork images
|
|
14
|
+
* - Exhibition table using CSS grid (year / title / venue / tag columns)
|
|
15
|
+
* - Press list with CSS ::before dot separator
|
|
16
|
+
* - Two-column contact grid
|
|
17
|
+
* - System font stack only (Inter → system-ui fallback) — no HTTP requests
|
|
18
|
+
* - picsum.photos for artwork images (placeholder seeded by id)
|
|
19
|
+
*
|
|
20
|
+
* Run: node examples/dev.server.js → http://localhost:3001/suzukibento
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Static data
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
const works = [
|
|
28
|
+
{ id: 1043, cls: 'sb-work--a', title: 'Erosion (Shibuya)', year: '2025', medium: 'Oil, ash, dust on canvas — 220×180cm' },
|
|
29
|
+
{ id: 1025, cls: 'sb-work--b', title: 'Fragments No. 14', year: '2024', medium: 'Mixed media — 140×140cm' },
|
|
30
|
+
{ id: 1015, cls: 'sb-work--c', title: 'Displaced', year: '2024', medium: 'Acrylic, collage — 160×120cm' },
|
|
31
|
+
{ id: 1059, cls: 'sb-work--d', title: 'Residue', year: '2023', medium: 'Oil on linen — 100×100cm' },
|
|
32
|
+
{ id: 1062, cls: 'sb-work--e', title: 'Mother Tongue', year: '2023', medium: 'Oil, gold leaf — 180×140cm' },
|
|
33
|
+
{ id: 1084, cls: 'sb-work--f', title: 'Untitled (Berlin VI)', year: '2025', medium: 'Concrete, pigment — 300×200cm' },
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
const exhibitions = [
|
|
37
|
+
{ year: '2026', title: 'Soft Architectures', venue: 'Kunsthalle Basel, CH', tag: 'Solo', solo: true },
|
|
38
|
+
{ year: '2025', title: 'The Weight of Surface', venue: 'KW Institute, Berlin', tag: 'Solo', solo: true },
|
|
39
|
+
{ year: '2025', title: 'Asia Now', venue: 'Monnaie de Paris', tag: 'Group', solo: false },
|
|
40
|
+
{ year: '2024', title: 'Memory as Material', venue: 'Hauser & Wirth, Hong Kong', tag: 'Solo', solo: true },
|
|
41
|
+
{ year: '2024', title: 'Berlin Biennale 13', venue: 'Hamburger Bahnhof', tag: 'Group', solo: false },
|
|
42
|
+
{ year: '2023', title: 'Quiet Geographies', venue: 'Mori Art Museum, Tokyo', tag: 'Solo', solo: true },
|
|
43
|
+
{ year: '2022', title: 'New Painting / New Asia', venue: 'Tate St Ives', tag: 'Group', solo: false },
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
const press = [
|
|
47
|
+
'Frieze', 'Artforum', 'ArtReview', 'S\u00fcddeutsche Zeitung',
|
|
48
|
+
'The Japan Times', 'Mousse', 'e-flux',
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Helpers
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
const esc = (s) => String(s).replace(/[&<>"']/g, (c) => (
|
|
56
|
+
{ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]
|
|
57
|
+
))
|
|
58
|
+
|
|
59
|
+
const work = (w) => `
|
|
60
|
+
<figure class="sb-work ${w.cls}">
|
|
61
|
+
<img src="/images/suzukibento/${w.id}.jpg" alt="${esc(w.title)}, ${esc(w.year)}" loading="lazy" width="1200" height="900">
|
|
62
|
+
<figcaption class="sb-work__caption">${esc(w.title)} <span>${esc(w.year)}</span></figcaption>
|
|
63
|
+
</figure>
|
|
64
|
+
`
|
|
65
|
+
|
|
66
|
+
const exhibition = (e) => `
|
|
67
|
+
<li class="sb-exhibition">
|
|
68
|
+
<span class="sb-exhibition__year">${esc(e.year)}</span>
|
|
69
|
+
<span class="sb-exhibition__title">${esc(e.title)}</span>
|
|
70
|
+
<span class="sb-exhibition__venue">${esc(e.venue)}</span>
|
|
71
|
+
<span class="sb-exhibition__tag${e.solo ? ' sb-exhibition__tag--solo' : ''}">${esc(e.tag)}</span>
|
|
72
|
+
</li>
|
|
73
|
+
`
|
|
74
|
+
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// Spec
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
export default {
|
|
80
|
+
route: '/suzukibento',
|
|
81
|
+
|
|
82
|
+
meta: {
|
|
83
|
+
title: 'Susuki Bento \u2014 Painter',
|
|
84
|
+
description: 'Tokyo-born, Berlin-based mixed-media artist exploring memory, displacement, and urban erosion through large-scale painting.',
|
|
85
|
+
theme: 'dark',
|
|
86
|
+
styles: ['/pulse-ui.css', '/examples/suzukibento.css'],
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
view: () => `
|
|
90
|
+
<a href="#main-content" class="skip-link">Skip to main content</a>
|
|
91
|
+
|
|
92
|
+
<header class="sb-nav">
|
|
93
|
+
<a href="/suzukibento" class="sb-nav__brand" aria-label="Susuki Bento — home">Susuki Bento<span class="sb-nav__dot" aria-hidden="true"></span></a>
|
|
94
|
+
<input type="checkbox" id="sb-menu-toggle" class="sb-nav__toggle-input" aria-hidden="true">
|
|
95
|
+
<label for="sb-menu-toggle" class="sb-nav__burger" aria-label="Toggle menu">
|
|
96
|
+
<span></span><span></span><span></span>
|
|
97
|
+
</label>
|
|
98
|
+
<nav aria-label="Primary">
|
|
99
|
+
<ul class="sb-nav__links">
|
|
100
|
+
<li><a href="#works">Works</a></li>
|
|
101
|
+
<li><a href="#about">About</a></li>
|
|
102
|
+
<li><a href="#exhibitions">Exhibitions</a></li>
|
|
103
|
+
<li><a href="#contact">Contact</a></li>
|
|
104
|
+
</ul>
|
|
105
|
+
</nav>
|
|
106
|
+
</header>
|
|
107
|
+
|
|
108
|
+
<main id="main-content">
|
|
109
|
+
<section class="sb-hero">
|
|
110
|
+
<span class="sb-hero__eyebrow">ペインタ \u2014 b. Tokyo, 1984 \u00b7 lives Berlin</span>
|
|
111
|
+
<h1 class="sb-hero__title">Susuki<br>Bento<em>.</em></h1>
|
|
112
|
+
<div class="sb-hero__meta">
|
|
113
|
+
<div>
|
|
114
|
+
<span class="sb-hero__meta-label">Now showing</span>
|
|
115
|
+
<strong>The Weight of Surface \u2014 KW Institute, Berlin</strong>
|
|
116
|
+
</div>
|
|
117
|
+
<div>
|
|
118
|
+
<span class="sb-hero__meta-label">Closing</span>
|
|
119
|
+
<strong>14 September 2026</strong>
|
|
120
|
+
</div>
|
|
121
|
+
<div>
|
|
122
|
+
<span class="sb-hero__meta-label">Represented by</span>
|
|
123
|
+
<strong>Hauser & Wirth</strong>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</section>
|
|
127
|
+
|
|
128
|
+
<section id="works" class="sb-section">
|
|
129
|
+
<div class="sb-section__head">
|
|
130
|
+
<span class="sb-section__label">01 \u2014 Selected works</span>
|
|
131
|
+
<h2 class="sb-section__title">Recent paintings, 2023\u20132025</h2>
|
|
132
|
+
</div>
|
|
133
|
+
<div class="sb-works">
|
|
134
|
+
${works.map(work).join('')}
|
|
135
|
+
</div>
|
|
136
|
+
</section>
|
|
137
|
+
|
|
138
|
+
<section id="about" class="sb-section">
|
|
139
|
+
<div class="sb-section__head">
|
|
140
|
+
<span class="sb-section__label">02 \u2014 Statement</span>
|
|
141
|
+
<h2 class="sb-section__title">On surface, memory, dust</h2>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="sb-statement">
|
|
144
|
+
<dl class="sb-statement__bio">
|
|
145
|
+
<dt>Born</dt><dd>Tokyo, 1984</dd>
|
|
146
|
+
<dt>Lives</dt><dd>Berlin-Wedding</dd>
|
|
147
|
+
<dt>Education</dt><dd>Tokyo University of the Arts, MFA<br>St\u00e4delschule, Frankfurt</dd>
|
|
148
|
+
<dt>Studio</dt><dd>Soldiner Stra\u00dfe 31, 13359 Berlin</dd>
|
|
149
|
+
</dl>
|
|
150
|
+
<div>
|
|
151
|
+
<p>The work begins with surface. I scrape, layer, bury and uncover until the paint behaves like architecture — weight, weather, decay. Each canvas is a record of what a place could not hold onto.</p>
|
|
152
|
+
<p>For two decades I have moved between Tokyo and Berlin, two cities defined by the things they have erased. My paintings collect this erasure: dust from demolition sites, charcoal from cleared lots, the colour of a wall before it was repainted. They are quiet objects made from loud absences.</p>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
</section>
|
|
156
|
+
|
|
157
|
+
<section id="exhibitions" class="sb-section">
|
|
158
|
+
<div class="sb-section__head">
|
|
159
|
+
<span class="sb-section__label">03 \u2014 Exhibition history</span>
|
|
160
|
+
<h2 class="sb-section__title">Selected solo & group, 2022\u20132026</h2>
|
|
161
|
+
</div>
|
|
162
|
+
<ul class="sb-exhibitions">
|
|
163
|
+
${exhibitions.map(exhibition).join('')}
|
|
164
|
+
</ul>
|
|
165
|
+
</section>
|
|
166
|
+
|
|
167
|
+
<section class="sb-section">
|
|
168
|
+
<div class="sb-section__head">
|
|
169
|
+
<span class="sb-section__label">04 \u2014 Press</span>
|
|
170
|
+
<h2 class="sb-section__title">In print</h2>
|
|
171
|
+
</div>
|
|
172
|
+
<p class="sb-press">${press.map((p) => `<span>${esc(p)}</span>`).join('')}</p>
|
|
173
|
+
</section>
|
|
174
|
+
|
|
175
|
+
<section id="contact" class="sb-section">
|
|
176
|
+
<div class="sb-section__head">
|
|
177
|
+
<span class="sb-section__label">05 \u2014 Contact</span>
|
|
178
|
+
<h2 class="sb-section__title">Studio & representation</h2>
|
|
179
|
+
</div>
|
|
180
|
+
<div class="sb-contact">
|
|
181
|
+
<div>
|
|
182
|
+
<h3>Studio enquiries</h3>
|
|
183
|
+
<a href="mailto:studio@susukibento.art">studio@susukibento.art</a>
|
|
184
|
+
<p>Soldiner Stra\u00dfe 31<br>13359 Berlin</p>
|
|
185
|
+
</div>
|
|
186
|
+
<div>
|
|
187
|
+
<h3>Gallery representation</h3>
|
|
188
|
+
<a href="mailto:bento@hauserwirth.com">bento@hauserwirth.com</a>
|
|
189
|
+
<p>Hauser & Wirth<br>Z\u00fcrich \u00b7 London \u00b7 Hong Kong</p>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
</section>
|
|
193
|
+
</main>
|
|
194
|
+
|
|
195
|
+
<footer class="sb-foot">
|
|
196
|
+
<span>© Susuki Bento Studio 2026</span>
|
|
197
|
+
<span>Site by <a href="https://invisibleloop.com" class="sb-foot__link" target="_blank" rel="noopener">invisibleloop</a></span>
|
|
198
|
+
</footer>
|
|
199
|
+
`,
|
|
200
|
+
}
|