BDS Toolkit
(Playground)
BDS - Settings
Base Settings
Adjust the values to generate the preview
BDS - Settings
Typography
LineHeights
Text Size
PX
var(--text-xs)
PX
var(--text-s)
PX
var(--text-m)
PX
var(--text-l)
PX
var(--text-xl)
Variables
Variables
Variables
Heading Size
PX
var(--heading-xs)
PX
var(--heading-s)
PX
var(--heading-m)
PX
var(--heading-l)
PX
var(--heading-xl)
Variables
Spacing
var(--space-4xl)
var(--space-3xl)
var(--space-2xl)
var(--space-xl)
var(--space-l)
var(--space-m)
var(--space-s)
var(--space-xs)
var(--space-2xs)
var(--space-3xs)
var(--space-4xs)
Variables
Gap
var(--gap-2xl)
var(--gap-xl)
var(--gap-l)
var(--gap-m)
var(--gap-s)
var(--gap-xs)
var(--gap-2xs)
Variables
Radius
var(--radius-xl)
PX
var(--radius-l)
PX
var(--radius-m)
PX
var(--radius-s)
PX
var(--radius-xs)
PX
Colors - Theme
Theme Colors
var(--primary)
var(--secondary)
var(--accent)
var(--background)
var(--gradient-a1)
var(--gradient-a2)
Gradient example
var(--light)
var(--dark)
Colors - Theme
Colors - Theme
Neutral Colors
var(--neutral-l2)
var(--neutral-l1)
var(--neutral)
var(--neutral-d1)
var(--neutral-d2)
Colors - Status
Colors - Status
Status Colors
var(--info-l1)
var(--info)
var(--info-d1)
var(--success-l1)
var(--success)
var(--success-d1)
var(--warning-l1)
var(--warning)
var(--warning-d1)
var(--danger-l1)
var(--danger)
var(--danger-d1)
var(--muted-l1)
var(--muted)
var(--muted-d1)
<div class="output-area">
<div class="card">
<div class="card-header">
<h2>Generated Stylesheet</h2>
<button id="copy-css-btn">Copy</button>
</div>
<pre><code id="stylesheet-output"></code></pre>
</div>
</div> document.addEventListener('DOMContentLoaded', () => {
const inputs = document.querySelectorAll('.config-panel input[data-var]');
const liveStylesElement = document.getElementById('live-preview-styles');
const outputElement = document.getElementById('stylesheet-output');
const copyBtn = document.getElementById('copy-css-btn');
const initialConfig = {
'--viewport-min-px': 320,
'--viewport-max-px': 1440,
'--root-size': 10,
'--text-min-px': 16,
'--text-max-px': 20,
'--text-ratio': 1.25,
'--heading-min-px': 24,
'--heading-max-px': 60,
'--heading-ratio': 1.333,
'--space-min-px': 16,
'--space-max-px': 32,
'--space-ratio': 1.5,
'--radius-min-px': 4,
'--radius-max-px': 24,
'--radius-ratio': 1.5,
'--gap-min-px': 16,
'--gap-max-px': 32,
'--gap-ratio': 1.5,
'--container-xs': 40,
'--container-s': 50,
'--container-m': 64,
'--container-l': 72,
'--container-xl': 80,
'--container-2xl': 90,
'--status-lighter': 15,
'--status-darker': 15,
'--neutral-lighter': 15,
'--neutral-lightest': 30,
'--neutral-darker': 15,
'--neutral-darkest': 30,
'--primary': '#3B82F6',
'--secondary': '#4B5563',
'--accent': '#EC4899',
'--background': '#F9FAFB',
'--gradient-100': '#E0E7FF',
'--gradient-900': '#3730A3',
'--light': '#ffffff',
'--dark': '#000000',
'--h-muted': 215, '--s-muted': 30, '--l-muted': 55,
'--h-success': 145, '--s-success': 65, '--l-success': 45,
'--h-warning': 45, '--s-warning': 90, '--l-warning': 55,
'--h-danger': 0, '--s-danger': 85, '--l-danger': 50,
'--h-info': 190, '--s-info': 80, '--l-info': 50,
'--h-neutral': 220, '--s-neutral': 10, '--l-neutral': 50,
};
function generateAndApplyStylesheet() {
const values = {};
// Gather all current values from inputs
inputs.forEach(input => {
values[input.dataset.var] = input.value;
});
// Add fixed values that are not in the form but are in the config
Object.keys(initialConfig).forEach(key => {
if (!values[key]) {
values[key] = initialConfig[key];
}
});
const v = {};
Object.keys(values).forEach(key => v[key] = parseFloat(values[key]));
const buildClamp = (minPx, maxPx, rootSize, vpMin, vpMax) => `clamp(calc(${minPx} / ${rootSize} * 1rem), calc((${minPx} / ${rootSize} * 1rem) + (${maxPx} - ${minPx}) * (100vw - ${vpMin}px) / (${vpMax} - ${vpMin})), calc(${maxPx} / ${rootSize} * 1rem))`;
let fullStylesheetContent = `:root {\n`;
// Add all config values to the output
for (const key in initialConfig) {
let unit = '';
if (key.startsWith('--s-') || key.startsWith('--l-') || key.includes('lighter') || key.includes('darker') || key.includes('lightest') || key.includes('darkest')) {
unit = '%';
} else if (key.startsWith('--container')) {
unit = 'rem';
}
fullStylesheetContent += ` ${key}: ${values[key]}${unit};\n`;
}
fullStylesheetContent += '\n /* Derived Colors */\n';
const colorTypes = ['muted', 'success', 'warning', 'danger', 'info'];
colorTypes.forEach(type => {
fullStylesheetContent += ` --${type}-l-1: hsl(var(--h-${type}), var(--s-${type}), calc(var(--l-${type}) + var(--status-lighter)));\n`;
fullStylesheetContent += ` --${type}: hsl(var(--h-${type}), var(--s-${type}), var(--l-${type}));\n`;
fullStylesheetContent += ` --${type}-d-1: hsl(var(--h-${type}), var(--s-${type}), calc(var(--l-${type}) - var(--status-darker)));\n`;
});
fullStylesheetContent += ` --neutral-l-2: hsl(var(--h-neutral), var(--s-neutral), calc(var(--l-neutral) + var(--neutral-lightest)));\n`;
fullStylesheetContent += ` --neutral-l-1: hsl(var(--h-neutral), var(--s-neutral), calc(var(--l-neutral) + var(--neutral-lighter)));\n`;
fullStylesheetContent += ` --neutral: hsl(var(--h-neutral), var(--s-neutral), var(--l-neutral));\n`;
fullStylesheetContent += ` --neutral-d-1: hsl(var(--h-neutral), var(--s-neutral), calc(var(--l-neutral) - var(--neutral-darker)));\n`;
fullStylesheetContent += ` --neutral-d-2: hsl(var(--h-neutral), var(--s-neutral), calc(var(--l-neutral) - var(--neutral-darkest)));\n`;
fullStylesheetContent += '\n /* Text Colors */\n';
fullStylesheetContent += ` --heading-color-10: var(--neutral-d2);\n`;
fullStylesheetContent += ` --text-color-10: var(--neutral-d1);\n`;
fullStylesheetContent += ` --text-invert: var(--light);\n`;
fullStylesheetContent += ` --heading-invert: var(--light);\n`;
fullStylesheetContent += '\n /* Base Units */\n';
fullStylesheetContent += ` --text-base-size: ${buildClamp(v['--text-min-px'], v['--text-max-px'], v['--root-size'], v['--viewport-min-px'], v['--viewport-max-px'])};\n`;
fullStylesheetContent += ` --heading-base-size: ${buildClamp(v['--heading-min-px'], v['--heading-max-px'], v['--root-size'], v['--viewport-min-px'], v['--viewport-max-px'])};\n`;
fullStylesheetContent += ` --space-base-unit: ${buildClamp(v['--space-min-px'], v['--space-max-px'], v['--root-size'], v['--viewport-min-px'], v['--viewport-max-px'])};\n`;
fullStylesheetContent += ` --radius-base-unit: ${buildClamp(v['--radius-min-px'], v['--radius-max-px'], v['--root-size'], v['--viewport-min-px'], v['--viewport-max-px'])};\n`;
fullStylesheetContent += ` --gap-base-unit: ${buildClamp(v['--gap-min-px'], v['--gap-max-px'], v['--root-size'], v['--viewport-min-px'], v['--viewport-max-px'])};\n`;
const scales = {
text: { base: 'var(--text-base-size)', ratio: 'var(--text-ratio)', sizes: ['xs', 's', 'm', 'l', 'xl'] },
heading: { base: 'var(--heading-base-size)', ratio: 'var(--heading-ratio)', sizes: ['xs', 's', 'm', 'l', 'xl'] },
space: { base: 'var(--space-base-unit)', ratio: 'var(--space-ratio)', sizes: ['4xs', '3xs', '2xs', 'xs', 's', 'm', 'l', 'xl', '2xl', '3xl', '4xl'] },
radius: { base: 'var(--radius-base-unit)', ratio: 'var(--radius-ratio)', sizes: ['xs', 's', 'm', 'l', 'xl'] },
gap: { base: 'var(--gap-base-unit)', ratio: 'var(--gap-ratio)', sizes: ['2xs', 'xs', 's', 'm', 'l', 'xl', '2xl'] }
};
fullStylesheetContent += '\n /* Full Scales */\n';
for (const [scaleName, config] of Object.entries(scales)) {
const mIndex = config.sizes.indexOf('m');
config.sizes.forEach((size, index) => {
if (size === 'm') {
fullStylesheetContent += ` --${scaleName}-m: ${config.base};\n`;
} else {
const operation = index < mIndex ? '/' : '*';
let calculation = `var(--${scaleName}-${config.sizes[index - (index < mIndex ? -1 : 1)]}) ${operation} ${config.ratio}`;
fullStylesheetContent += ` --${scaleName}-${size}: calc(${calculation});\n`;
}
});
}
fullStylesheetContent += '\n /* Line Heights */\n';
fullStylesheetContent += ` --line-height-text-xs: calc(var(--text-xs) * 1.5);\n`;
fullStylesheetContent += ` --line-height-text-s: calc(var(--text-s) * 1.5);\n`;
fullStylesheetContent += ` --line-height-text-m: calc(var(--text-m) * 1.6);\n`;
fullStylesheetContent += ` --line-height-text-l: calc(var(--text-l) * 1.5);\n`;
fullStylesheetContent += ` --line-height-text-xl: calc(var(--text-xl) * 1.4);\n`;
fullStylesheetContent += ` --line-height-heading-xs: calc(var(--heading-xs) * 1.3);\n`;
fullStylesheetContent += ` --line-height-heading-s: calc(var(--heading-s) * 1.3);\n`;
fullStylesheetContent += ` --line-height-heading-m: calc(var(--heading-m) * 1.2);\n`;
fullStylesheetContent += ` --line-height-heading-l: calc(var(--heading-l) * 1.2);\n`;
fullStylesheetContent += ` --line-height-heading-xl: calc(var(--heading-xl) * 1.15);\n`;
fullStylesheetContent += '}';
if (liveStylesElement) {
liveStylesElement.textContent = fullStylesheetContent.replace(':root', '.demo-area');
}
if (outputElement) {
outputElement.textContent = fullStylesheetContent;
}
}
function initializeApp() {
inputs.forEach(input => {
if (initialConfig.hasOwnProperty(input.dataset.var)) {
input.value = initialConfig[input.dataset.var];
}
});
generateAndApplyStylesheet();
}
function handleInputChange(e) {
const variableName = e.target.dataset.var;
let value = e.target.value;
let unit = '';
if (variableName.startsWith('--s-') || variableName.startsWith('--l-') || variableName.includes('lighter') || variableName.includes('darker') || variableName.includes('lightest') || variableName.includes('darkest')) {
unit = '%';
} else if (variableName.startsWith('--container')) {
unit = 'rem';
}
generateAndApplyStylesheet();
}
function copyToClipboard() {
const textToCopy = outputElement.textContent;
const tempTextArea = document.createElement('textarea');
tempTextArea.value = textToCopy;
document.body.appendChild(tempTextArea);
tempTextArea.select();
document.execCommand('copy');
document.body.removeChild(tempTextArea);
copyBtn.textContent = 'Copied!';
setTimeout(() => { copyBtn.textContent = 'Copy'; }, 2000);
}
initializeApp();
inputs.forEach(input => {
input.addEventListener('input', handleInputChange);
});
copyBtn.addEventListener('click', copyToClipboard);
});