@stacklist-app/brandkit 1.1.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 +95 -0
- package/bin/brandkit.js +49 -0
- package/cli/build.js +35 -0
- package/cli/dev.js +129 -0
- package/cli/generate.js +272 -0
- package/cli/init.js +62 -0
- package/dist/engine.js +847 -0
- package/dist/index.html +173 -0
- package/dist/styles.css +995 -0
- package/integrations/astro.js +26 -0
- package/integrations/vite.js +81 -0
- package/lib/config-schema.js +219 -0
- package/lib/extract-css.js +117 -0
- package/lib/extract-logos.js +98 -0
- package/lib/extract-tailwind.js +146 -0
- package/lib/generate-helpers.js +320 -0
- package/lib/resolve.js +11 -0
- package/lib/template.js +84 -0
- package/package.json +37 -0
package/dist/engine.js
ADDED
|
@@ -0,0 +1,847 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brandkit Rendering Engine
|
|
3
|
+
* Loads config.json → bootstraps theme → renders all sections → wires interactivity
|
|
4
|
+
*/
|
|
5
|
+
(async function () {
|
|
6
|
+
/* ================================================================
|
|
7
|
+
0. Load config
|
|
8
|
+
================================================================ */
|
|
9
|
+
var res = await fetch('./config.json');
|
|
10
|
+
var config = await res.json();
|
|
11
|
+
init(config);
|
|
12
|
+
|
|
13
|
+
function init(cfg) {
|
|
14
|
+
var copyFormat = localStorage.getItem('brandkit-copy-format') || 'hex';
|
|
15
|
+
|
|
16
|
+
/* ==============================================================
|
|
17
|
+
HTML escape helper — prevents XSS from config values
|
|
18
|
+
============================================================== */
|
|
19
|
+
function esc(s) {
|
|
20
|
+
return String(s || '').replace(/&/g, '&').replace(/</g, '<')
|
|
21
|
+
.replace(/>/g, '>').replace(/"/g, '"');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/* ==============================================================
|
|
25
|
+
BOOTSTRAP — inject fonts + CSS variables before any rendering
|
|
26
|
+
============================================================== */
|
|
27
|
+
function bootstrap() {
|
|
28
|
+
// Inject Google Fonts
|
|
29
|
+
if (cfg.fonts) {
|
|
30
|
+
var families = [];
|
|
31
|
+
if (cfg.fonts.display && cfg.fonts.display.googleImport) families.push(cfg.fonts.display.googleImport);
|
|
32
|
+
if (cfg.fonts.body && cfg.fonts.body.googleImport) families.push(cfg.fonts.body.googleImport);
|
|
33
|
+
if (families.length) {
|
|
34
|
+
var link = document.createElement('link');
|
|
35
|
+
link.rel = 'stylesheet';
|
|
36
|
+
link.href = 'https://fonts.googleapis.com/css2?family=' + families.join('&family=') + '&display=swap';
|
|
37
|
+
document.head.appendChild(link);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Inject CSS custom properties from theme
|
|
42
|
+
if (cfg.theme) {
|
|
43
|
+
var vars = [];
|
|
44
|
+
var keys = Object.keys(cfg.theme);
|
|
45
|
+
for (var i = 0; i < keys.length; i++) {
|
|
46
|
+
vars.push(' ' + keys[i] + ': ' + cfg.theme[keys[i]] + ';');
|
|
47
|
+
}
|
|
48
|
+
// Add font variables from config
|
|
49
|
+
if (cfg.fonts) {
|
|
50
|
+
if (cfg.fonts.display) vars.push(" --font-display: '" + cfg.fonts.display.family + "', sans-serif;");
|
|
51
|
+
if (cfg.fonts.body) vars.push(" --font-body: '" + cfg.fonts.body.family + "', sans-serif;");
|
|
52
|
+
}
|
|
53
|
+
var style = document.createElement('style');
|
|
54
|
+
style.setAttribute('data-brandkit-theme', '');
|
|
55
|
+
style.textContent = ':root {\n' + vars.join('\n') + '\n}';
|
|
56
|
+
document.head.appendChild(style);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Set page title
|
|
60
|
+
if (cfg.brand && cfg.brand.displayName) {
|
|
61
|
+
document.title = cfg.brand.displayName + ' \u2014 Brand Guide';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* ==============================================================
|
|
66
|
+
1. Toast
|
|
67
|
+
============================================================== */
|
|
68
|
+
function toast(message) {
|
|
69
|
+
var container = document.getElementById('toast-container');
|
|
70
|
+
if (!container) return;
|
|
71
|
+
var el = document.createElement('div');
|
|
72
|
+
el.className = 'toast';
|
|
73
|
+
el.innerHTML =
|
|
74
|
+
'<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5">' +
|
|
75
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>' +
|
|
76
|
+
'</svg>' +
|
|
77
|
+
message;
|
|
78
|
+
container.appendChild(el);
|
|
79
|
+
setTimeout(function () {
|
|
80
|
+
el.classList.add('out');
|
|
81
|
+
setTimeout(function () { el.remove(); }, 200);
|
|
82
|
+
}, 2000);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function copyText(text) {
|
|
86
|
+
try {
|
|
87
|
+
await navigator.clipboard.writeText(text);
|
|
88
|
+
toast('Copied: ' + (text.length > 50 ? text.slice(0, 47) + '...' : text));
|
|
89
|
+
} catch (_) {
|
|
90
|
+
toast('Copy failed \u2014 try from localhost or HTTPS');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* ==============================================================
|
|
95
|
+
2. Render navigation
|
|
96
|
+
============================================================== */
|
|
97
|
+
function renderNav() {
|
|
98
|
+
var navEl = document.getElementById('nav');
|
|
99
|
+
if (!navEl || !cfg.nav) return;
|
|
100
|
+
navEl.innerHTML = cfg.nav.map(function (group) {
|
|
101
|
+
var header = '<li class="nav-group">' + esc(group.group) + '</li>';
|
|
102
|
+
var items = group.items.map(function (item) {
|
|
103
|
+
return '<li class="nav-group-items"><a href="#' + esc(item.id) + '">' + esc(item.label) + '</a></li>';
|
|
104
|
+
}).join('');
|
|
105
|
+
return header + items;
|
|
106
|
+
}).join('');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* ==============================================================
|
|
110
|
+
3. Render colors
|
|
111
|
+
============================================================== */
|
|
112
|
+
function renderColorGrid(colors, containerId) {
|
|
113
|
+
var container = document.getElementById(containerId);
|
|
114
|
+
if (!container) return;
|
|
115
|
+
if (!colors || !colors.length) {
|
|
116
|
+
container.innerHTML = '<div class="empty-state">No colors configured</div>';
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
container.innerHTML = colors.map(function (c) {
|
|
120
|
+
// Field aliases for resilience
|
|
121
|
+
var name = c.name || c.label || '';
|
|
122
|
+
var hex = c.hex || '';
|
|
123
|
+
var oklch = c.oklch || '';
|
|
124
|
+
var cssVar = c.cssVar || c.token || '';
|
|
125
|
+
var role = c.role || c.usage || '';
|
|
126
|
+
var isLight = c.light || (hex && parseInt(hex.slice(1), 16) > 0xAAAAAA);
|
|
127
|
+
var valueDisplay = oklch ? (esc(hex) + ' \u00B7 ' + esc(oklch)) : esc(hex);
|
|
128
|
+
return (
|
|
129
|
+
'<div class="color-card">' +
|
|
130
|
+
'<div class="color-swatch copyable ' + (isLight ? 'has-border' : '') + '"' +
|
|
131
|
+
' style="background:' + esc(hex) + ';"' +
|
|
132
|
+
' data-hex="' + esc(hex) + '"' +
|
|
133
|
+
' data-oklch="' + esc(oklch) + '"' +
|
|
134
|
+
' data-css-var="' + esc(cssVar) + '"' +
|
|
135
|
+
' tabindex="0" role="button" aria-label="Copy ' + esc(name) + ' color value">' +
|
|
136
|
+
'<div class="copy-hint"><span>Click to copy</span></div>' +
|
|
137
|
+
'</div>' +
|
|
138
|
+
'<div class="color-name">' + esc(name) + '</div>' +
|
|
139
|
+
'<div class="color-value copyable" data-copy="' + esc(hex) + '">' + valueDisplay + '</div>' +
|
|
140
|
+
'<div class="color-role">' + esc(role) + '</div>' +
|
|
141
|
+
'</div>'
|
|
142
|
+
);
|
|
143
|
+
}).join('');
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function renderColors() {
|
|
147
|
+
if (!cfg.colors) return;
|
|
148
|
+
// Support both { label, items } objects and plain arrays
|
|
149
|
+
var groups = ['brand', 'neutrals', 'semantic'];
|
|
150
|
+
groups.forEach(function (key) {
|
|
151
|
+
var group = cfg.colors[key];
|
|
152
|
+
if (!group) return;
|
|
153
|
+
var items = group.items || group;
|
|
154
|
+
var label = group.label || key.charAt(0).toUpperCase() + key.slice(1);
|
|
155
|
+
// Set the group label if the container's preceding label exists
|
|
156
|
+
var labelEl = document.getElementById('colors-' + key + '-label');
|
|
157
|
+
if (labelEl) labelEl.textContent = label;
|
|
158
|
+
renderColorGrid(items, 'colors-' + key);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* ==============================================================
|
|
163
|
+
4. Render gradients
|
|
164
|
+
============================================================== */
|
|
165
|
+
function renderGradients() {
|
|
166
|
+
var stopsContainer = document.getElementById('gradient-stops');
|
|
167
|
+
if (!stopsContainer || !cfg.gradients || !cfg.gradients.length) return;
|
|
168
|
+
var brand = cfg.gradients[0];
|
|
169
|
+
if (brand && brand.stops && brand.stops.length) {
|
|
170
|
+
stopsContainer.innerHTML = brand.stops.map(function (s) {
|
|
171
|
+
return (
|
|
172
|
+
'<div class="gradient-stop">' +
|
|
173
|
+
'<div class="gradient-stop-dot" style="background:' + s.color + ';"></div>' +
|
|
174
|
+
s.name + ' \u00B7 ' + s.position +
|
|
175
|
+
'</div>'
|
|
176
|
+
);
|
|
177
|
+
}).join('');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* ==============================================================
|
|
182
|
+
5. Render logos
|
|
183
|
+
============================================================== */
|
|
184
|
+
function renderLogos() {
|
|
185
|
+
var grid = document.getElementById('logos-grid');
|
|
186
|
+
if (!grid) return;
|
|
187
|
+
if (!cfg.logos || !cfg.logos.length) {
|
|
188
|
+
grid.innerHTML = '<div class="empty-state">No logos configured. Add logo files to the logos/ directory and update config.json.</div>';
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
var downloadIcon =
|
|
193
|
+
'<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">' +
|
|
194
|
+
'<path stroke-linecap="round" stroke-linejoin="round" d="M12 4v12m0 0l-4-4m4 4l4-4M4 18h16"/>' +
|
|
195
|
+
'</svg>';
|
|
196
|
+
|
|
197
|
+
grid.innerHTML = cfg.logos.map(function (logo, idx) {
|
|
198
|
+
// Determine background class
|
|
199
|
+
var bgClass = '';
|
|
200
|
+
var bgStyle = '';
|
|
201
|
+
if (logo.background === 'light') {
|
|
202
|
+
bgClass = 'on-light';
|
|
203
|
+
bgStyle = 'background:#FFFFFF; border:1px solid var(--mist);';
|
|
204
|
+
} else if (logo.background === 'dark') {
|
|
205
|
+
bgClass = 'on-dark';
|
|
206
|
+
bgStyle = 'background:var(--ink);';
|
|
207
|
+
} else if (logo.background === 'gradient') {
|
|
208
|
+
bgClass = 'on-gradient';
|
|
209
|
+
bgStyle = 'background:var(--gradient-brand);';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Available formats
|
|
213
|
+
var formats = Object.keys(logo.variants);
|
|
214
|
+
var firstFormat = formats[0] || 'svg';
|
|
215
|
+
|
|
216
|
+
// Preview image: use svg first, then png, then jpg
|
|
217
|
+
var previewSrc = logo.variants.svg || logo.variants.png || logo.variants.jpg || '';
|
|
218
|
+
|
|
219
|
+
// Format toggle buttons
|
|
220
|
+
var formatToggles = formats.map(function (fmt, fi) {
|
|
221
|
+
return (
|
|
222
|
+
'<button class="logo-format-btn' + (fi === 0 ? ' active' : '') + '"' +
|
|
223
|
+
' data-logo-idx="' + idx + '"' +
|
|
224
|
+
' data-format="' + fmt + '">' +
|
|
225
|
+
fmt.toUpperCase() +
|
|
226
|
+
'</button>'
|
|
227
|
+
);
|
|
228
|
+
}).join('');
|
|
229
|
+
|
|
230
|
+
// Size picker (only visible for raster formats)
|
|
231
|
+
var sizeOptions = (cfg.logoSizes || []).map(function (s) {
|
|
232
|
+
return '<option value="' + (s.width || '') + '">' + s.label + '</option>';
|
|
233
|
+
}).join('');
|
|
234
|
+
|
|
235
|
+
var isFirstRaster = (firstFormat !== 'svg');
|
|
236
|
+
|
|
237
|
+
var sizePicker =
|
|
238
|
+
'<select class="logo-size-picker" data-logo-idx="' + idx + '"' +
|
|
239
|
+
(isFirstRaster ? '' : ' style="display:none;"') + '>' +
|
|
240
|
+
sizeOptions +
|
|
241
|
+
'</select>';
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
'<div class="logo-card ' + bgClass + '" style="' + bgStyle + '" data-logo-idx="' + idx + '">' +
|
|
245
|
+
'<img src="' + previewSrc + '" alt="' + logo.name + '">' +
|
|
246
|
+
'<div class="logo-name">' + logo.name + '</div>' +
|
|
247
|
+
'<div class="logo-description">' + (logo.description || '') + '</div>' +
|
|
248
|
+
'<div class="logo-controls">' +
|
|
249
|
+
'<div class="logo-format-toggle">' + formatToggles + '</div>' +
|
|
250
|
+
sizePicker +
|
|
251
|
+
'<button class="logo-download-btn" data-logo-idx="' + idx + '">' +
|
|
252
|
+
downloadIcon + ' Download' +
|
|
253
|
+
'</button>' +
|
|
254
|
+
'</div>' +
|
|
255
|
+
'</div>'
|
|
256
|
+
);
|
|
257
|
+
}).join('');
|
|
258
|
+
|
|
259
|
+
// Wire up format toggles
|
|
260
|
+
grid.addEventListener('click', function (e) {
|
|
261
|
+
var fmtBtn = e.target.closest('.logo-format-btn');
|
|
262
|
+
if (fmtBtn) {
|
|
263
|
+
var card = fmtBtn.closest('.logo-card');
|
|
264
|
+
card.querySelectorAll('.logo-format-btn').forEach(function (b) {
|
|
265
|
+
b.classList.toggle('active', b === fmtBtn);
|
|
266
|
+
});
|
|
267
|
+
var picker = card.querySelector('.logo-size-picker');
|
|
268
|
+
if (picker) {
|
|
269
|
+
picker.style.display = (fmtBtn.dataset.format === 'svg') ? 'none' : '';
|
|
270
|
+
}
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Download button
|
|
275
|
+
var dlBtn = e.target.closest('.logo-download-btn');
|
|
276
|
+
if (dlBtn) {
|
|
277
|
+
var logoIndex = parseInt(dlBtn.dataset.logoIdx, 10);
|
|
278
|
+
var logoData = cfg.logos[logoIndex];
|
|
279
|
+
var cardEl = dlBtn.closest('.logo-card');
|
|
280
|
+
|
|
281
|
+
var activeBtn = cardEl.querySelector('.logo-format-btn.active');
|
|
282
|
+
var format = activeBtn ? activeBtn.dataset.format : Object.keys(logoData.variants)[0];
|
|
283
|
+
|
|
284
|
+
var filePath = logoData.variants[format];
|
|
285
|
+
if (!filePath) return;
|
|
286
|
+
|
|
287
|
+
var slug = cfg.brand.name || 'brand';
|
|
288
|
+
var namePart = logoData.name.toLowerCase()
|
|
289
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
290
|
+
.replace(/(^-|-$)/g, '');
|
|
291
|
+
|
|
292
|
+
if (format === 'svg') {
|
|
293
|
+
triggerDirectDownload(filePath, slug + '-' + namePart + '.svg');
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
var sizePicker2 = cardEl.querySelector('.logo-size-picker');
|
|
298
|
+
var targetWidth = sizePicker2 ? parseInt(sizePicker2.value, 10) : NaN;
|
|
299
|
+
|
|
300
|
+
if (!targetWidth || isNaN(targetWidth)) {
|
|
301
|
+
var ext = format === 'png' ? '.png' : '.jpg';
|
|
302
|
+
triggerDirectDownload(filePath, slug + '-' + namePart + ext);
|
|
303
|
+
} else {
|
|
304
|
+
resizeAndDownload(filePath, format, targetWidth, slug + '-' + namePart + '-' + targetWidth + 'px');
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function triggerDirectDownload(href, filename) {
|
|
311
|
+
var a = document.createElement('a');
|
|
312
|
+
a.href = href;
|
|
313
|
+
a.download = filename;
|
|
314
|
+
document.body.appendChild(a);
|
|
315
|
+
a.click();
|
|
316
|
+
document.body.removeChild(a);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function resizeAndDownload(src, format, targetWidth, filenameBase) {
|
|
320
|
+
var img = new Image();
|
|
321
|
+
img.crossOrigin = 'anonymous';
|
|
322
|
+
img.onload = function () {
|
|
323
|
+
var ratio = img.naturalHeight / img.naturalWidth;
|
|
324
|
+
var targetHeight = Math.round(targetWidth * ratio);
|
|
325
|
+
var canvas = document.createElement('canvas');
|
|
326
|
+
canvas.width = targetWidth;
|
|
327
|
+
canvas.height = targetHeight;
|
|
328
|
+
var ctx = canvas.getContext('2d');
|
|
329
|
+
ctx.drawImage(img, 0, 0, targetWidth, targetHeight);
|
|
330
|
+
var mimeType = format === 'png' ? 'image/png' : 'image/jpeg';
|
|
331
|
+
var ext = format === 'png' ? '.png' : '.jpg';
|
|
332
|
+
canvas.toBlob(function (blob) {
|
|
333
|
+
if (!blob) return;
|
|
334
|
+
var url = URL.createObjectURL(blob);
|
|
335
|
+
triggerDirectDownload(url, filenameBase + ext);
|
|
336
|
+
setTimeout(function () { URL.revokeObjectURL(url); }, 1000);
|
|
337
|
+
}, mimeType, 0.92);
|
|
338
|
+
};
|
|
339
|
+
img.onerror = function () {
|
|
340
|
+
toast('Failed to load image for resize');
|
|
341
|
+
};
|
|
342
|
+
img.src = src;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/* ==============================================================
|
|
346
|
+
6. Render typography
|
|
347
|
+
============================================================== */
|
|
348
|
+
function renderTypography() {
|
|
349
|
+
var typeScale = document.getElementById('type-scale');
|
|
350
|
+
if (!typeScale) return;
|
|
351
|
+
if (!cfg.typography || !cfg.typography.length) {
|
|
352
|
+
typeScale.innerHTML = '<div class="empty-state">No type scale configured</div>';
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
typeScale.innerHTML = cfg.typography.map(function (t) {
|
|
357
|
+
var family = t.font === 'display'
|
|
358
|
+
? cfg.fonts.display.family
|
|
359
|
+
: cfg.fonts.body.family;
|
|
360
|
+
|
|
361
|
+
var specParts = [t.size, t.weight, t.tracking !== '0' ? t.tracking : null, t.leading ? t.leading + ' leading' : null, family].filter(Boolean);
|
|
362
|
+
|
|
363
|
+
var displaySize = Math.min(parseInt(t.size, 10), 48);
|
|
364
|
+
var inlineStyle =
|
|
365
|
+
"font-family:'" + family + "',sans-serif;" +
|
|
366
|
+
'font-size:' + displaySize + 'px;' +
|
|
367
|
+
'font-weight:' + t.weight + ';' +
|
|
368
|
+
'letter-spacing:' + t.tracking + ';' +
|
|
369
|
+
'line-height:' + t.leading + ';' +
|
|
370
|
+
(t.uppercase ? 'text-transform:uppercase;' : '') +
|
|
371
|
+
(t.name === 'Body SM' ? 'color:var(--graphite);' : '') +
|
|
372
|
+
(t.name === 'Caption' || t.name === 'Overline' ? 'color:var(--slate);' : '');
|
|
373
|
+
|
|
374
|
+
return (
|
|
375
|
+
'<div class="type-row">' +
|
|
376
|
+
'<span class="type-row-label">' + t.name + '</span>' +
|
|
377
|
+
'<span class="type-row-sample" style="' + inlineStyle + '">' + t.sample + '</span>' +
|
|
378
|
+
'<span class="type-row-spec">' + specParts.join(' \u00B7 ') + '</span>' +
|
|
379
|
+
'</div>'
|
|
380
|
+
);
|
|
381
|
+
}).join('');
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/* ==============================================================
|
|
385
|
+
7. Render voice
|
|
386
|
+
============================================================== */
|
|
387
|
+
function renderVoice() {
|
|
388
|
+
var voiceGrid = document.getElementById('voice-grid');
|
|
389
|
+
if (!voiceGrid || !cfg.voice) return;
|
|
390
|
+
|
|
391
|
+
voiceGrid.innerHTML =
|
|
392
|
+
'<div class="voice-card do">' +
|
|
393
|
+
'<div class="voice-card-label">' + cfg.brand.displayName + ' says</div>' +
|
|
394
|
+
cfg.voice.do.map(function (v) {
|
|
395
|
+
return '<div class="voice-example">' + v + '</div>';
|
|
396
|
+
}).join('') +
|
|
397
|
+
'</div>' +
|
|
398
|
+
'<div class="voice-card dont">' +
|
|
399
|
+
'<div class="voice-card-label">' + cfg.brand.displayName + ' never says</div>' +
|
|
400
|
+
cfg.voice.dont.map(function (v) {
|
|
401
|
+
return '<div class="voice-example">' + v + '</div>';
|
|
402
|
+
}).join('') +
|
|
403
|
+
'</div>';
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/* ==============================================================
|
|
407
|
+
8. Render components
|
|
408
|
+
============================================================== */
|
|
409
|
+
function renderComponents() {
|
|
410
|
+
var container = document.getElementById('components-content');
|
|
411
|
+
if (!container) return;
|
|
412
|
+
if (!cfg.components) {
|
|
413
|
+
container.innerHTML = '<div class="empty-state">No component patterns configured</div>';
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
var html = '';
|
|
418
|
+
|
|
419
|
+
// Buttons
|
|
420
|
+
if (cfg.components.buttons) {
|
|
421
|
+
cfg.components.buttons.forEach(function (group) {
|
|
422
|
+
html += '<div class="component-label">' + group.variant + '</div>';
|
|
423
|
+
group.items.forEach(function (item) {
|
|
424
|
+
html += '<div class="component-row">';
|
|
425
|
+
item.sizes.forEach(function (size, i) {
|
|
426
|
+
html +=
|
|
427
|
+
'<button class="btn ' + item.class + ' btn-' + size + '">' +
|
|
428
|
+
item.labels[i] +
|
|
429
|
+
'</button>';
|
|
430
|
+
});
|
|
431
|
+
html += '</div>';
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Cards
|
|
437
|
+
if (cfg.components.cards) {
|
|
438
|
+
html += '<div class="component-label" style="margin-top:28px;">Cards \u2014 Light Background</div>';
|
|
439
|
+
html += '<div class="card-demo-grid">';
|
|
440
|
+
cfg.components.cards.forEach(function (card) {
|
|
441
|
+
var cardTitle = card.title || card.label || '';
|
|
442
|
+
var cardDesc = card.description || '';
|
|
443
|
+
var cardTag = card.tag || '';
|
|
444
|
+
html +=
|
|
445
|
+
'<div class="card-demo">' +
|
|
446
|
+
'<h4>' + cardTitle + '</h4>' +
|
|
447
|
+
'<p>' + cardDesc + '</p>' +
|
|
448
|
+
(cardTag ? '<span class="tag">' + cardTag + '</span>' : '') +
|
|
449
|
+
'</div>';
|
|
450
|
+
});
|
|
451
|
+
html += '</div>';
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Stats
|
|
455
|
+
if (cfg.components.stats) {
|
|
456
|
+
html += '<div class="component-label" style="margin-top:28px;">Stats \u2014 Dark Background</div>';
|
|
457
|
+
html += '<div class="dark-card-demo">';
|
|
458
|
+
html += '<div class="dark-section-label">By the numbers</div>';
|
|
459
|
+
html += '<div class="dark-card-demo-grid">';
|
|
460
|
+
cfg.components.stats.forEach(function (stat) {
|
|
461
|
+
html +=
|
|
462
|
+
'<div class="dark-card">' +
|
|
463
|
+
'<h4>' + stat.value + '</h4>' +
|
|
464
|
+
'<p>' + stat.label + '</p>' +
|
|
465
|
+
'</div>';
|
|
466
|
+
});
|
|
467
|
+
html += '</div></div>';
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
container.innerHTML = html;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/* ==============================================================
|
|
474
|
+
9. Render spacing
|
|
475
|
+
============================================================== */
|
|
476
|
+
function renderSpacing() {
|
|
477
|
+
var scale = document.getElementById('spacing-scale');
|
|
478
|
+
if (!scale) return;
|
|
479
|
+
var data = cfg.spacing || [
|
|
480
|
+
{ px: 4, token: 'space-1' }, { px: 8, token: 'space-2' }, { px: 12, token: 'space-3' },
|
|
481
|
+
{ px: 16, token: 'space-4' }, { px: 24, token: 'space-6' }, { px: 32, token: 'space-8' },
|
|
482
|
+
{ px: 48, token: 'space-12' }, { px: 64, token: 'space-16' }, { px: 96, token: 'space-24' }
|
|
483
|
+
];
|
|
484
|
+
scale.innerHTML = data.map(function (s) {
|
|
485
|
+
return (
|
|
486
|
+
'<div class="spacing-row">' +
|
|
487
|
+
'<span class="spacing-label">' + s.px + 'px</span>' +
|
|
488
|
+
'<div class="spacing-bar" style="width:' + Math.min(s.px * 4, 400) + 'px;">' +
|
|
489
|
+
'<span class="spacing-value">' + s.token + '</span>' +
|
|
490
|
+
'</div>' +
|
|
491
|
+
'</div>'
|
|
492
|
+
);
|
|
493
|
+
}).join('');
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/* ==============================================================
|
|
497
|
+
10. Render accessibility
|
|
498
|
+
============================================================== */
|
|
499
|
+
function renderAccessibility() {
|
|
500
|
+
var grid = document.getElementById('a11y-grid');
|
|
501
|
+
if (!grid) return;
|
|
502
|
+
if (!cfg.accessibility || !cfg.accessibility.length) {
|
|
503
|
+
grid.innerHTML = '<div class="empty-state">No accessibility pairs configured</div>';
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
grid.innerHTML = cfg.accessibility.map(function (a) {
|
|
508
|
+
// Field aliases
|
|
509
|
+
var bg = a.bg || a.background || '#FFFFFF';
|
|
510
|
+
var fg = a.fg || a.foreground || '#000000';
|
|
511
|
+
var bgName = a.bgName || '';
|
|
512
|
+
var fgName = a.fgName || '';
|
|
513
|
+
var rating = a.rating || a.level || '';
|
|
514
|
+
var ratio = a.ratio || '';
|
|
515
|
+
// Split "pair" field if bgName/fgName missing
|
|
516
|
+
if (!bgName && !fgName && a.pair) {
|
|
517
|
+
var parts = a.pair.split(' on ');
|
|
518
|
+
if (parts.length === 2) { fgName = parts[0]; bgName = parts[1]; }
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
var ratingClass =
|
|
522
|
+
(rating === 'AAA' || rating === 'AA') ? 'pass' :
|
|
523
|
+
rating === 'AA Large' ? 'large' : 'fail';
|
|
524
|
+
var border = a.border ? 'border:1px solid var(--mist);' : '';
|
|
525
|
+
var textSize = a.largeText ? 'font-size:20px;font-weight:600;' : '';
|
|
526
|
+
|
|
527
|
+
return (
|
|
528
|
+
'<div class="a11y-card" style="background:' + bg + ';color:' + fg + ';' + border + '">' +
|
|
529
|
+
'<div class="a11y-text" style="' + textSize + '">' + fgName + ' on ' + bgName + '</div>' +
|
|
530
|
+
'<div class="a11y-meta">' +
|
|
531
|
+
'<span class="a11y-ratio">' + ratio + '</span>' +
|
|
532
|
+
'<span class="a11y-badge ' + ratingClass + '">' + rating + '</span>' +
|
|
533
|
+
'</div>' +
|
|
534
|
+
'</div>'
|
|
535
|
+
);
|
|
536
|
+
}).join('');
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/* ==============================================================
|
|
540
|
+
11. Render CSS variables
|
|
541
|
+
============================================================== */
|
|
542
|
+
function renderCSSVars() {
|
|
543
|
+
var codeBlock = document.getElementById('code-block');
|
|
544
|
+
if (!codeBlock) return;
|
|
545
|
+
if (!cfg.cssVariables || !cfg.cssVariables.length) {
|
|
546
|
+
codeBlock.innerHTML = '<span class="code-section">/* No CSS variables configured */</span>';
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
codeBlock.innerHTML = cfg.cssVariables.map(function (section) {
|
|
551
|
+
var sectionName = section.section || section.name || 'Variables';
|
|
552
|
+
var vars = section.vars || [];
|
|
553
|
+
var sectionComment = '<span class="code-section">/* \u2500\u2500 ' + sectionName + ' \u2500\u2500 */</span>';
|
|
554
|
+
var lines = vars.map(function (v) {
|
|
555
|
+
// Field aliases
|
|
556
|
+
var prop = v.prop || v.var || v.name || '';
|
|
557
|
+
var value = v.value || '';
|
|
558
|
+
var comment = v.comment || v.usage || '';
|
|
559
|
+
var copyVal = prop + ': ' + value + ';';
|
|
560
|
+
var commentHtml = comment ? ' <span class="token-comment">/* ' + comment + ' */</span>' : '';
|
|
561
|
+
return (
|
|
562
|
+
'<span class="code-line" data-copy="' + copyVal.replace(/"/g, '"') + '">' +
|
|
563
|
+
'<span class="token-prop">' + prop + '</span>: ' +
|
|
564
|
+
'<span class="token-value">' + value + '</span>;' +
|
|
565
|
+
commentHtml +
|
|
566
|
+
'</span>'
|
|
567
|
+
);
|
|
568
|
+
}).join('\n');
|
|
569
|
+
return sectionComment + '\n' + lines;
|
|
570
|
+
}).join('\n\n');
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/* ==============================================================
|
|
574
|
+
12. Render hierarchy (from config)
|
|
575
|
+
============================================================== */
|
|
576
|
+
function renderHierarchy() {
|
|
577
|
+
var container = document.getElementById('hierarchy-content');
|
|
578
|
+
if (!container) return;
|
|
579
|
+
if (!cfg.hierarchy || !cfg.hierarchy.length) {
|
|
580
|
+
container.innerHTML = '<div class="empty-state">No text hierarchy configured</div>';
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
var demoHtml = '<div class="hierarchy-demo">';
|
|
585
|
+
cfg.hierarchy.forEach(function (h) {
|
|
586
|
+
demoHtml += '<p class="' + h.class + '">' + h.description + '</p>';
|
|
587
|
+
});
|
|
588
|
+
demoHtml += '</div>';
|
|
589
|
+
|
|
590
|
+
var labelsHtml = '<div class="hierarchy-labels">';
|
|
591
|
+
cfg.hierarchy.forEach(function (h) {
|
|
592
|
+
labelsHtml +=
|
|
593
|
+
'<div class="hierarchy-label">' +
|
|
594
|
+
'<div class="hierarchy-dot" style="background: var(' + h.colorVar + ');"></div> ' +
|
|
595
|
+
h.colorName + ': ' + h.hex +
|
|
596
|
+
'</div>';
|
|
597
|
+
});
|
|
598
|
+
labelsHtml += '</div>';
|
|
599
|
+
|
|
600
|
+
container.innerHTML = demoHtml + labelsHtml;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/* ==============================================================
|
|
604
|
+
13. Render section intros (from config)
|
|
605
|
+
============================================================== */
|
|
606
|
+
function renderSectionIntros() {
|
|
607
|
+
if (!cfg.sections) return;
|
|
608
|
+
var mapping = {
|
|
609
|
+
'gradients': 'section-intro-gradients',
|
|
610
|
+
'logos': 'section-intro-logos',
|
|
611
|
+
'components': 'section-intro-components',
|
|
612
|
+
'spacing': 'section-intro-spacing',
|
|
613
|
+
'variables': 'section-intro-variables'
|
|
614
|
+
};
|
|
615
|
+
var keys = Object.keys(mapping);
|
|
616
|
+
for (var i = 0; i < keys.length; i++) {
|
|
617
|
+
var el = document.getElementById(mapping[keys[i]]);
|
|
618
|
+
if (el && cfg.sections[keys[i]]) {
|
|
619
|
+
el.textContent = cfg.sections[keys[i]];
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
// Gradient text demo
|
|
623
|
+
var gradTextEl = document.getElementById('gradient-text-demo');
|
|
624
|
+
if (gradTextEl && cfg.sections.gradientTextDemo) {
|
|
625
|
+
gradTextEl.textContent = cfg.sections.gradientTextDemo;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/* ==============================================================
|
|
630
|
+
14. Copy to clipboard (initCopy)
|
|
631
|
+
============================================================== */
|
|
632
|
+
function initCopy() {
|
|
633
|
+
document.addEventListener('click', function (e) {
|
|
634
|
+
// Color swatch
|
|
635
|
+
var swatch = e.target.closest('.color-swatch.copyable');
|
|
636
|
+
if (swatch) {
|
|
637
|
+
var value = swatch.dataset[copyFormat] || swatch.dataset.hex;
|
|
638
|
+
copyText(value);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Color value text
|
|
643
|
+
var colorVal = e.target.closest('.color-value.copyable');
|
|
644
|
+
if (colorVal) {
|
|
645
|
+
copyText(colorVal.dataset.copy || colorVal.textContent.trim());
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// Gradient display
|
|
650
|
+
var gradientCopy = e.target.closest('.gradient-display.copyable');
|
|
651
|
+
if (gradientCopy) {
|
|
652
|
+
copyText(gradientCopy.dataset.copy);
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Code lines
|
|
657
|
+
var codeLine = e.target.closest('.code-line');
|
|
658
|
+
if (codeLine) {
|
|
659
|
+
copyText(codeLine.dataset.copy);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/* ==============================================================
|
|
666
|
+
15. Copy format bar
|
|
667
|
+
============================================================== */
|
|
668
|
+
function initFormatBar() {
|
|
669
|
+
var bar = document.querySelector('.copy-format-bar');
|
|
670
|
+
if (!bar) return;
|
|
671
|
+
|
|
672
|
+
// Restore saved format
|
|
673
|
+
bar.querySelectorAll('button').forEach(function (b) {
|
|
674
|
+
b.classList.toggle('active', b.dataset.format === copyFormat);
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
bar.addEventListener('click', function (e) {
|
|
678
|
+
var btn = e.target.closest('button');
|
|
679
|
+
if (!btn) return;
|
|
680
|
+
copyFormat = btn.dataset.format;
|
|
681
|
+
localStorage.setItem('brandkit-copy-format', copyFormat);
|
|
682
|
+
bar.querySelectorAll('button').forEach(function (b) {
|
|
683
|
+
b.classList.toggle('active', b === btn);
|
|
684
|
+
});
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/* ==============================================================
|
|
689
|
+
16. Section navigation (IntersectionObserver)
|
|
690
|
+
============================================================== */
|
|
691
|
+
function initNav() {
|
|
692
|
+
var sections = document.querySelectorAll('.section[id]');
|
|
693
|
+
var navLinks = document.querySelectorAll('#nav a');
|
|
694
|
+
|
|
695
|
+
if (!sections.length || !navLinks.length) return;
|
|
696
|
+
|
|
697
|
+
var observer = new IntersectionObserver(function (entries) {
|
|
698
|
+
entries.forEach(function (entry) {
|
|
699
|
+
if (entry.isIntersecting) {
|
|
700
|
+
navLinks.forEach(function (link) {
|
|
701
|
+
link.classList.toggle('active', link.getAttribute('href') === '#' + entry.target.id);
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
}, { rootMargin: '-20% 0px -70% 0px' });
|
|
706
|
+
|
|
707
|
+
sections.forEach(function (section) { observer.observe(section); });
|
|
708
|
+
|
|
709
|
+
navLinks.forEach(function (link) {
|
|
710
|
+
link.addEventListener('click', function (e) {
|
|
711
|
+
e.preventDefault();
|
|
712
|
+
var target = document.querySelector(link.getAttribute('href'));
|
|
713
|
+
if (target) target.scrollIntoView({ behavior: 'smooth' });
|
|
714
|
+
});
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/* ==============================================================
|
|
719
|
+
17. Type tester
|
|
720
|
+
============================================================== */
|
|
721
|
+
function initTypeTester() {
|
|
722
|
+
var testerFont = document.getElementById('type-tester-font');
|
|
723
|
+
var testerInput = document.getElementById('type-tester-input');
|
|
724
|
+
if (!testerFont || !testerInput) return;
|
|
725
|
+
|
|
726
|
+
testerFont.addEventListener('change', function () {
|
|
727
|
+
testerInput.style.fontFamily = "'" + testerFont.value + "', sans-serif";
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/* ==============================================================
|
|
732
|
+
18. Render shell (header, intro, footer, misc)
|
|
733
|
+
============================================================== */
|
|
734
|
+
function renderShell() {
|
|
735
|
+
// Header
|
|
736
|
+
var wordmark = document.getElementById('header-wordmark');
|
|
737
|
+
if (wordmark) wordmark.textContent = cfg.brand.name;
|
|
738
|
+
var meta = document.getElementById('header-meta');
|
|
739
|
+
if (meta) meta.innerHTML = 'Web Style Guide v' + cfg.brand.version + '<br>' + cfg.brand.date;
|
|
740
|
+
|
|
741
|
+
// Intro
|
|
742
|
+
var intro = document.getElementById('intro');
|
|
743
|
+
if (intro) intro.textContent = cfg.brand.description;
|
|
744
|
+
|
|
745
|
+
// Voice intro
|
|
746
|
+
var voiceIntro = document.getElementById('voice-intro');
|
|
747
|
+
if (voiceIntro && cfg.voice) voiceIntro.textContent = cfg.voice.description;
|
|
748
|
+
|
|
749
|
+
// Footer
|
|
750
|
+
var footer = document.getElementById('footer');
|
|
751
|
+
if (footer) footer.innerHTML =
|
|
752
|
+
'<span>' + cfg.brand.url + ' \u00B7 ' + cfg.brand.byline + '</span>' +
|
|
753
|
+
'<span>Web Style Guide v' + cfg.brand.version + ' \u00B7 ' + cfg.brand.date + '</span>';
|
|
754
|
+
|
|
755
|
+
// Typography specimens — read descriptions from config
|
|
756
|
+
var typeDisplayName = document.getElementById('type-display-name');
|
|
757
|
+
if (typeDisplayName && cfg.fonts) typeDisplayName.textContent = cfg.fonts.display.family;
|
|
758
|
+
var typeDisplayDesc = document.getElementById('type-display-desc');
|
|
759
|
+
if (typeDisplayDesc && cfg.fonts && cfg.fonts.display.description) {
|
|
760
|
+
typeDisplayDesc.textContent = cfg.fonts.display.description;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
var typeBodyName = document.getElementById('type-body-name');
|
|
764
|
+
if (typeBodyName && cfg.fonts) typeBodyName.textContent = cfg.fonts.body.family;
|
|
765
|
+
var typeBodyDesc = document.getElementById('type-body-desc');
|
|
766
|
+
if (typeBodyDesc && cfg.fonts && cfg.fonts.body.description) {
|
|
767
|
+
typeBodyDesc.textContent = cfg.fonts.body.description;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// Type tester font options
|
|
771
|
+
var testerFont = document.getElementById('type-tester-font');
|
|
772
|
+
var testerInput = document.getElementById('type-tester-input');
|
|
773
|
+
if (testerFont && cfg.fonts) {
|
|
774
|
+
testerFont.innerHTML =
|
|
775
|
+
'<option value="' + cfg.fonts.display.family + '">' + cfg.fonts.display.family + '</option>' +
|
|
776
|
+
'<option value="' + cfg.fonts.body.family + '">' + cfg.fonts.body.family + '</option>';
|
|
777
|
+
if (testerInput) {
|
|
778
|
+
testerInput.style.fontFamily = "'" + cfg.fonts.display.family + "', sans-serif";
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// Gradient data-copy attributes, inline styles, and labels
|
|
783
|
+
var gradBrand = document.getElementById('gradient-brand-copy');
|
|
784
|
+
if (gradBrand && cfg.gradients && cfg.gradients[0]) {
|
|
785
|
+
gradBrand.dataset.copy = cfg.gradients[0].css;
|
|
786
|
+
gradBrand.style.background = cfg.gradients[0].css;
|
|
787
|
+
}
|
|
788
|
+
var gradBrandLabel = document.getElementById('gradient-brand-label');
|
|
789
|
+
if (gradBrandLabel && cfg.gradients && cfg.gradients[0]) {
|
|
790
|
+
var g0desc = cfg.gradients[0].description || cfg.gradients[0].usage || '';
|
|
791
|
+
gradBrandLabel.textContent = cfg.gradients[0].name + ' Gradient \u00B7 135\u00B0 \u00B7 ' + g0desc;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
var gradSubtle = document.getElementById('gradient-subtle-copy');
|
|
795
|
+
if (gradSubtle && cfg.gradients && cfg.gradients[1]) {
|
|
796
|
+
gradSubtle.dataset.copy = cfg.gradients[1].css;
|
|
797
|
+
gradSubtle.style.background = cfg.gradients[1].css;
|
|
798
|
+
}
|
|
799
|
+
var gradSubtleLabel = document.getElementById('gradient-subtle-label');
|
|
800
|
+
if (gradSubtleLabel && cfg.gradients && cfg.gradients[1]) {
|
|
801
|
+
var g1desc = cfg.gradients[1].description || cfg.gradients[1].usage || '';
|
|
802
|
+
gradSubtleLabel.textContent = cfg.gradients[1].name + ' Gradient \u00B7 135\u00B0 \u00B7 ' + g1desc;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// Gradient usage do/don't
|
|
806
|
+
var gradUsage = document.getElementById('gradient-usage');
|
|
807
|
+
if (gradUsage && cfg.gradientUsage) {
|
|
808
|
+
gradUsage.innerHTML =
|
|
809
|
+
'<div class="gradient-usage-card do">' +
|
|
810
|
+
'<h4>Use gradient for</h4>' +
|
|
811
|
+
'<ul>' + cfg.gradientUsage.do.map(function (item) { return '<li>' + item + '</li>'; }).join('') + '</ul>' +
|
|
812
|
+
'</div>' +
|
|
813
|
+
'<div class="gradient-usage-card dont">' +
|
|
814
|
+
"<h4>Don't use gradient for</h4>" +
|
|
815
|
+
'<ul>' + cfg.gradientUsage.dont.map(function (item) { return '<li>' + item + '</li>'; }).join('') + '</ul>' +
|
|
816
|
+
'</div>';
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Sidebar brand name
|
|
820
|
+
var sidebarBrand = document.querySelector('.sidebar-brand');
|
|
821
|
+
if (sidebarBrand) sidebarBrand.textContent = 'brandkit';
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/* ==============================================================
|
|
825
|
+
Execute all
|
|
826
|
+
============================================================== */
|
|
827
|
+
bootstrap();
|
|
828
|
+
renderShell();
|
|
829
|
+
renderNav();
|
|
830
|
+
renderSectionIntros();
|
|
831
|
+
renderColors();
|
|
832
|
+
renderGradients();
|
|
833
|
+
renderLogos();
|
|
834
|
+
renderTypography();
|
|
835
|
+
renderHierarchy();
|
|
836
|
+
renderVoice();
|
|
837
|
+
renderComponents();
|
|
838
|
+
renderSpacing();
|
|
839
|
+
renderAccessibility();
|
|
840
|
+
renderCSSVars();
|
|
841
|
+
|
|
842
|
+
initCopy();
|
|
843
|
+
initFormatBar();
|
|
844
|
+
initNav();
|
|
845
|
+
initTypeTester();
|
|
846
|
+
}
|
|
847
|
+
})();
|