diff --git a/packages/@luke-ui/react/src/icon-button/index.tsx b/packages/@luke-ui/react/src/icon-button/index.tsx
index 5811c4b0..96f952e2 100644
--- a/packages/@luke-ui/react/src/icon-button/index.tsx
+++ b/packages/@luke-ui/react/src/icon-button/index.tsx
@@ -53,7 +53,7 @@ export function IconButton(props: IconButtonProps): JSX.Element {
isPending={isPending}
size={size}
>
-
+
);
}
diff --git a/packages/@luke-ui/react/src/loading-skeleton/index.tsx b/packages/@luke-ui/react/src/loading-skeleton/index.tsx
index 88b44bf2..5dc3bf03 100644
--- a/packages/@luke-ui/react/src/loading-skeleton/index.tsx
+++ b/packages/@luke-ui/react/src/loading-skeleton/index.tsx
@@ -77,7 +77,7 @@ export function LoadingSkeleton(props: LoadingSkeletonProps): ReactNode {
*`, {
+globalStyleInLayer('recipes', `${loadingSkeletonClassName}:not([data-skeleton-inline]) > *`, {
...surface,
...pulse,
borderRadius: `var(${skeletonRadiusVar}, 0px)`,
@@ -87,7 +87,7 @@ globalStyleInLayer('recipes', `${loadingSkeleton}:not([data-skeleton-inline]) >
position: 'relative !important' as 'relative',
});
-globalStyleInLayer('recipes', `${loadingSkeleton}:not([data-skeleton-inline]) > * *`, {
+globalStyleInLayer('recipes', `${loadingSkeletonClassName}:not([data-skeleton-inline]) > * *`, {
'@media': {
'(forced-colors: active)': forcedColorsSurface,
},
@@ -96,11 +96,15 @@ globalStyleInLayer('recipes', `${loadingSkeleton}:not([data-skeleton-inline]) >
// A pseudo-element painted over the child covers visuals the forced styles can't reach (nested backgrounds,
// rounded corners); `inset: -1px` also covers the child's border box edges.
-globalStyleInLayer('recipes', `${loadingSkeleton}:not([data-skeleton-inline]) > *::after`, {
- ...surface,
- ...pulse,
- borderRadius: `var(${skeletonRadiusVar}, 0px)`,
- content: '""',
- inset: '-1px',
- position: 'absolute',
-});
+globalStyleInLayer(
+ 'recipes',
+ `${loadingSkeletonClassName}:not([data-skeleton-inline]) > *::after`,
+ {
+ ...surface,
+ ...pulse,
+ borderRadius: `var(${skeletonRadiusVar}, 0px)`,
+ content: '""',
+ inset: '-1px',
+ position: 'absolute',
+ },
+);
diff --git a/packages/@luke-ui/react/src/recipes/recipe.ts b/packages/@luke-ui/react/src/recipes/recipe.ts
index e12a59a4..af7a227e 100644
--- a/packages/@luke-ui/react/src/recipes/recipe.ts
+++ b/packages/@luke-ui/react/src/recipes/recipe.ts
@@ -54,7 +54,6 @@ interface SinglePartConfig {
variants?: Variants;
defaultVariants?: VariantSelection;
compoundVariants?: Array>;
- extend?: SinglePartConfig;
}
/** The runtime function a single-part `recipe()` returns. */
@@ -73,19 +72,11 @@ type SlotVariantSelection> = {
-readonly [Group in keyof Variants]?: BooleanMap | undefined;
};
-/** A compound variant for a slotted recipe, whose style is keyed by slot. */
-interface SlotCompoundVariant> {
- variants: SlotVariantSelection;
- style: SlotStyles;
-}
-
/** Slotted recipe config. */
interface MultiPartConfig> {
slots: Record;
variants?: Variants;
defaultVariants?: SlotVariantSelection;
- compoundVariants?: Array>;
- extend?: MultiPartConfig>;
}
/** A single slot function: takes an optional extra class and returns a class string. */
@@ -111,10 +102,6 @@ export interface SlottedConfigInput {
slots: Record;
variants?: Record>>;
defaultVariants?: Record;
- compoundVariants?: ReadonlyArray<{
- variants: Record;
- style: Record;
- }>;
}
/** Derives the outer variant selection type for a built recipe. */
@@ -175,45 +162,16 @@ function registerSerializer(fn: object, importName: string, args: ReadonlyArray<
}
function buildSinglePart(config: SinglePartConfig): BuiltRecipe {
- const resolved = config.extend ? mergeSingleConfigs(config.extend, config) : config;
-
return recipeInRecipesLayer({
- ...(resolved.base === undefined ? {} : { base: resolved.base }),
- ...(resolved.variants === undefined ? {} : { variants: resolved.variants }),
- ...(resolved.defaultVariants === undefined
- ? {}
- : { defaultVariants: resolved.defaultVariants }),
- ...(resolved.compoundVariants === undefined
- ? {}
- : { compoundVariants: resolved.compoundVariants }),
+ ...(config.base === undefined ? {} : { base: config.base }),
+ ...(config.variants === undefined ? {} : { variants: config.variants }),
+ ...(config.defaultVariants === undefined ? {} : { defaultVariants: config.defaultVariants }),
+ ...(config.compoundVariants === undefined ? {} : { compoundVariants: config.compoundVariants }),
});
}
-function mergeSingleConfigs(
- base: SinglePartConfig,
- override: SinglePartConfig,
-): SinglePartConfig {
- const variants: VariantGroups = { ...base.variants };
- if (override.variants !== undefined) {
- for (const [group, values] of Object.entries(override.variants)) {
- variants[group] = { ...variants[group], ...values };
- }
- }
-
- const mergedBase = override.base ?? base.base;
-
- return {
- ...(mergedBase === undefined ? {} : { base: mergedBase }),
- ...(Object.keys(variants).length > 0 ? { variants } : {}),
- defaultVariants: { ...base.defaultVariants, ...override.defaultVariants },
- compoundVariants: [...(base.compoundVariants ?? []), ...(override.compoundVariants ?? [])],
- };
-}
-
function buildSlottedDescriptor(config: AnyMultiPartConfig): SlottedRecipeDescriptor {
- const resolved = config.extend ? mergeConfigs(config.extend, withoutExtend(config)) : config;
-
- const slotNames = Object.keys(resolved.slots);
+ const slotNames = Object.keys(config.slots);
const slots: Record = {};
const slotGroups: Record> = {};
@@ -221,8 +179,8 @@ function buildSlottedDescriptor(config: AnyMultiPartConfig): SlottedRecipeDescri
const variants: Record> = {};
const groupsForSlot: Array = [];
- if (resolved.variants !== undefined) {
- for (const [group, values] of Object.entries(resolved.variants)) {
+ if (config.variants !== undefined) {
+ for (const [group, values] of Object.entries(config.variants)) {
const slotValues: Record = {};
let hasSlot = false;
@@ -241,26 +199,14 @@ function buildSlottedDescriptor(config: AnyMultiPartConfig): SlottedRecipeDescri
}
}
- const compoundVariants: Array<{ variants: Record; style: RecipeStyleRule }> =
- [];
- if (resolved.compoundVariants !== undefined) {
- for (const compound of resolved.compoundVariants) {
- const style = compound.style[slotName];
- if (style !== undefined) {
- compoundVariants.push({ variants: compound.variants, style });
- }
- }
- }
-
- const defaultVariants = pickGroups(resolved.defaultVariants, groupsForSlot);
+ const defaultVariants = pickGroups(config.defaultVariants, groupsForSlot);
slots[slotName] = recipeInRecipesLayer({
- base: resolved.slots[slotName],
+ base: config.slots[slotName],
...(groupsForSlot.length > 0 ? { variants } : {}),
...(defaultVariants !== undefined && Object.keys(defaultVariants).length > 0
? { defaultVariants }
: {}),
- ...(compoundVariants.length > 0 ? { compoundVariants } : {}),
});
slotGroups[slotName] = groupsForSlot;
}
@@ -336,7 +282,7 @@ function recipeInRecipesLayer(options: RecipeInLayerOptions): BuiltRecipe {
}
// ---------------------------------------------------------------------------
-// extend (single-base inheritance)
+// Config shape detection
// ---------------------------------------------------------------------------
function isMultiPart(
@@ -349,55 +295,6 @@ function isObject(value: unknown): value is Record {
return typeof value === 'object' && value !== null;
}
-function withoutExtend(config: AnyMultiPartConfig): AnyMultiPartConfig {
- const { extend: _extend, ...rest } = config;
- return rest;
-}
-
-/**
- * Merges a slotted config's single `extend` base into it. Slots, variant groups,
- * variant values, per-slot variant styles, and default variants are combined. The
- * extending config takes precedence on same-named keys, and compound variants are
- * concatenated. Internal to `extend` resolution.
- */
-function mergeConfigs(...configs: Array): AnyMultiPartConfig {
- const slots: Record = {};
- const variants: Record>> = {};
- const defaultVariants: SlotVariantSelection> = {};
- const compoundVariants: Array>> = [];
-
- for (const config of configs) {
- const resolved = config.extend ? mergeConfigs(config.extend, withoutExtend(config)) : config;
-
- Object.assign(slots, resolved.slots);
-
- if (resolved.variants !== undefined) {
- for (const [group, values] of Object.entries(resolved.variants)) {
- const mergedGroup = variants[group] ?? {};
- for (const [value, slotStyles] of Object.entries(values)) {
- mergedGroup[value] = { ...mergedGroup[value], ...slotStyles };
- }
- variants[group] = mergedGroup;
- }
- }
-
- if (resolved.defaultVariants !== undefined) {
- Object.assign(defaultVariants, resolved.defaultVariants);
- }
-
- if (resolved.compoundVariants !== undefined) {
- compoundVariants.push(...resolved.compoundVariants);
- }
- }
-
- return {
- slots,
- ...(Object.keys(variants).length > 0 ? { variants } : {}),
- ...(Object.keys(defaultVariants).length > 0 ? { defaultVariants } : {}),
- ...(compoundVariants.length > 0 ? { compoundVariants } : {}),
- };
-}
-
// ---------------------------------------------------------------------------
// Runtime (referenced by the function serializer at import time)
// ---------------------------------------------------------------------------
diff --git a/packages/@luke-ui/react/src/recipes/text-input.css.ts b/packages/@luke-ui/react/src/recipes/text-input.css.ts
index ff2cfbc1..1985cf35 100644
--- a/packages/@luke-ui/react/src/recipes/text-input.css.ts
+++ b/packages/@luke-ui/react/src/recipes/text-input.css.ts
@@ -4,7 +4,7 @@ import {
composeInputStateSelectors,
descendantDisabledSelector,
inputStates,
-} from './input-states.css.js';
+} from './input-states.js';
import type { RecipeSelection, SlottedConfigInput } from './recipe.js';
import { recipe } from './recipe.js';
diff --git a/packages/@luke-ui/react/src/recipes/text.css.ts b/packages/@luke-ui/react/src/recipes/text.css.ts
index 435254ef..8a755e14 100644
--- a/packages/@luke-ui/react/src/recipes/text.css.ts
+++ b/packages/@luke-ui/react/src/recipes/text.css.ts
@@ -7,43 +7,6 @@ import type { RecipeSelection } from './recipe.js';
import { recipe } from './recipe.js';
import { visuallyHiddenStyle } from './visually-hidden.css.js';
-/** Typography size steps. */
-export type TextSize = FontSizeStep;
-
-/** Semantic text colours. */
-export type TextColor =
- | 'primary'
- | 'secondary'
- | 'accent'
- | 'info'
- | 'success'
- | 'warning'
- | 'danger';
-/** Semantic font-weight roles. */
-export type TextFontWeight = 'body' | 'label' | 'heading' | 'emphasis';
-/** Logical text alignment values. */
-export type TextAlign = 'start' | 'center' | 'end';
-/** Text wrapping values. */
-export type TextWrap = 'unset' | 'balance' | 'pretty';
-
-const textDecorationKeys = ['none', 'underline', 'line-through', 'inherit'] as const;
-/** Text decoration variant values. */
-export type TextDecoration = (typeof textDecorationKeys)[number];
-
-const textTransformKeys = ['none', 'capitalize', 'uppercase', 'lowercase', 'inherit'] as const;
-/** Text transform variant values. */
-export type TextTransform = (typeof textTransformKeys)[number];
-
-const fontVariantNumericKeys = [
- 'unset',
- 'diagonal-fractions',
- 'ordinal',
- 'slashed-zero',
- 'tabular-nums',
-] as const;
-/** Numeric glyph variant values. */
-export type TextFontVariantNumeric = (typeof fontVariantNumericKeys)[number];
-
const lineClampNone = {} satisfies ComplexStyleRule;
const lineClampSingleLine = {
display: 'block',
@@ -73,9 +36,6 @@ const lineClampVariants = {
5: lineClampMultiLine(5),
} as const;
-/** Text line-clamp variant values. */
-export type TextLineClampVariant = keyof typeof lineClampVariants;
-
const base = styleInLayer('recipes', {
color: vars.color.text.primary,
fontFamily: vars.font.family,
@@ -109,7 +69,7 @@ const sizeVariants = Object.fromEntries(
lineHeight: vars.font[size].lineHeight,
},
]),
-) as Record;
+) as Record;
const sizeStepCompoundVariants = fontSizeSteps.map((size) => {
const { baselineTrim, capHeightTrim, fontSize, lineHeight } = vars.font[size];
diff --git a/packages/@luke-ui/react/src/styles/stylesheet-contract.test.ts b/packages/@luke-ui/react/src/styles/stylesheet-contract.test.ts
index e1aab068..6abfe552 100644
--- a/packages/@luke-ui/react/src/styles/stylesheet-contract.test.ts
+++ b/packages/@luke-ui/react/src/styles/stylesheet-contract.test.ts
@@ -20,7 +20,7 @@ test('builds the public stylesheet with the retained layer contract', async () =
const stylesheet = await readFile(new URL('../../dist/stylesheet.css', import.meta.url), 'utf8');
const recipes = await import('@luke-ui/react/recipes');
const styles = await import('@luke-ui/react/styles');
- const recipeClasses = [...recipes.icon().split(' '), recipes.loadingSkeleton];
+ const recipeClasses = [...recipes.icon().split(' '), recipes.loadingSkeletonClassName];
const textClassesBySize = Object.fromEntries(
fontSizeSteps.map((size) => [size, recipes.text({ size }).split(' ')]),
) as TextClassesBySize;