Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/enhance-quick-ref.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'helixir': patch
'@helixir/core': patch
---

fix: quick-ref CSS snippet uses meaningful values instead of misleading 'initial', adds slot styling guidance
36 changes: 32 additions & 4 deletions packages/core/src/handlers/quick-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,22 +76,50 @@ function buildCssSnippet(tagName: string, meta: ComponentMetadata): string {
if (meta.cssProperties.length > 0) {
const propLines = meta.cssProperties
.slice(0, 5)
.map((p) => ` ${p.name}: initial;`)
.map((p) => {
const value = p.default ?? guessDefaultValue(p.name);
return ` ${p.name}: ${value};`;
})
.join('\n');
parts.push(`${tagName} {\n${propLines}\n}`);
parts.push(`/* Token customization */\n${tagName} {\n${propLines}\n}`);
}

if (meta.cssParts.length > 0) {
const partBlocks = meta.cssParts
.slice(0, 3)
.map((p) => `${tagName}::part(${p.name}) {\n /* ${p.description || p.name} */\n}`)
.join('\n\n');
parts.push(partBlocks);
parts.push(`/* Part-based customization */\n${partBlocks}`);
}

if (meta.slots.length > 0) {
const slotLines: string[] = [];
const hasDefaultSlot = meta.slots.some((s) => s.name === '');
const namedSlots = meta.slots.filter((s) => s.name !== '');

if (hasDefaultSlot) {
slotLines.push(`${tagName} > * { /* default slot content */ }`);
}
for (const slot of namedSlots.slice(0, 3)) {
slotLines.push(`${tagName} [slot="${slot.name}"] { /* ${slot.description || slot.name} */ }`);
}

parts.push(`/* Slot styling — target in light DOM CSS */\n${slotLines.join('\n')}`);
}

return parts.join('\n\n');
}

function guessDefaultValue(propName: string): string {
const lower = propName.toLowerCase();
if (/color|bg|background/.test(lower)) return '#value';
if (/size|font/.test(lower)) return '1rem';
if (/radius/.test(lower)) return '4px';
if (/spacing|padding|margin|gap/.test(lower)) return '1rem';
if (/shadow/.test(lower)) return '0 1px 2px rgba(0,0,0,.1)';
return '#value';
}

// ─── Main Entry Point ───────────────────────────────────────────────────────

export function getComponentQuickRef(meta: ComponentMetadata): ComponentQuickRef {
Expand Down Expand Up @@ -131,7 +159,7 @@ export function getComponentQuickRef(meta: ComponentMetadata): ComponentQuickRef
const cssProperties: QuickRefCssProperty[] = meta.cssProperties.map((p) => ({
name: p.name,
description: p.description,
example: `${p.name}: initial;`,
example: `${p.name}: ${p.default ?? guessDefaultValue(p.name)};`,
}));

const cssParts: QuickRefCssPart[] = meta.cssParts.map((p) => ({
Expand Down
29 changes: 28 additions & 1 deletion tests/handlers/quick-ref.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,10 +149,12 @@ describe('getComponentQuickRef — attribute details', () => {
// ─── CSS Styling Details ─────────────────────────────────────────────────────

describe('getComponentQuickRef — CSS details', () => {
it('CSS properties include example usage', () => {
it('CSS properties include example usage with meaningful value', () => {
const ref = getComponentQuickRef(buttonMeta);
const bg = ref.cssProperties.find((p) => p.name === '--my-button-bg');
expect(bg?.example).toContain('--my-button-bg');
// Should NOT use 'initial' — it's misleading for custom properties
expect(bg?.example).not.toContain('initial');
});

it('CSS parts include ::part() selector example', () => {
Expand All @@ -167,6 +169,31 @@ describe('getComponentQuickRef — CSS details', () => {
expect(ref.cssSnippet).toContain('--my-button-bg');
expect(ref.cssSnippet).toContain('::part(base)');
});

it('cssSnippet does NOT use "initial" for custom property values', () => {
const ref = getComponentQuickRef(buttonMeta);
expect(ref.cssSnippet).not.toContain(': initial;');
});

it('cssSnippet includes slot styling section when slots exist', () => {
const ref = getComponentQuickRef(buttonMeta);
expect(ref.cssSnippet).toContain('Slot styling');
expect(ref.cssSnippet).toContain('[slot="prefix"]');
});

it('cssSnippet omits slot styling for components without slots', () => {
const ref = getComponentQuickRef(bareComponent);
expect(ref.cssSnippet).not.toContain('Slot styling');
});

it('CSS property example uses CEM default when available', () => {
const metaWithDefault: ComponentMetadata = {
...bareComponent,
cssProperties: [{ name: '--my-bg', description: 'Background', default: '#fff' }],
};
const ref = getComponentQuickRef(metaWithDefault);
expect(ref.cssProperties[0]?.example).toContain('#fff');
});
});

// ─── Bare Component ──────────────────────────────────────────────────────────
Expand Down
Loading