Borders
Overview
The border composables help you create comprehensive border systems with minimal code. They generate border-related variables that can be easily referenced throughout your application, enabling flexible theming and consistent visual boundaries for your components.
Why use border composables?
Border composables help you:
- Centralize border definitions: Define all your border styles, widths, and colors in one place for easy management.
- Enable flexible theming: Override border variables to instantly update component borders across your application.
- Maintain consistency: Use semantic names to ensure consistent border usage throughout your design system.
- Reduce repetition: Reference border variables instead of repeating CSS values throughout your stylesheets.
- Separate concerns: Keep border colors semantically distinct from other color usage in your application.
useBorderStyle
The useBorderStyle()
function creates a set of border style variables covering all standard CSS border styles.
import { styleframe } from 'styleframe';
import { useBorderStyle } from '@styleframe/theme';
const s = styleframe();
const {
borderStyle,
borderStyleNone,
borderStyleSolid,
borderStyleDashed,
borderStyleDotted,
borderStyleDouble,
borderStyleGroove,
borderStyleInset,
borderStyleOutset,
} = useBorderStyle(s);
export default s;
:root {
--border-style--none: none;
--border-style--solid: solid;
--border-style--dashed: dashed;
--border-style--dotted: dotted;
--border-style--double: double;
--border-style--groove: groove;
--border-style--inset: inset;
--border-style--outset: outset;
--border-style: var(--border-style--solid);
}
The function creates variables for all standard CSS border styles and a default --border-style
variable that references solid
by default.
Creating Custom Border Style Variables
You can provide completely custom border style values if you need styles beyond the CSS standard keywords:
import { styleframe } from 'styleframe';
import { useBorderStyle } from '@styleframe/theme';
const s = styleframe();
const {
borderStyle,
borderStyleWavy,
borderStyleHidden
} = useBorderStyle(s, {
default: 'solid',
wavy: 'wavy', // Custom CSS value (if supported)
hidden: 'hidden'
});
export default s;
:root {
--border-style--wavy: wavy;
--border-style--hidden: hidden;
--border-style: solid;
}
Updating the Default Border Style Variable
You can override the default border style value after creating it by using variable()
:
import { styleframe } from 'styleframe';
import { useBorderStyle } from '@styleframe/theme';
const s = styleframe();
const { variable } = s;
const { borderStyle } = useBorderStyle(s);
// Override the default border style
variable(borderStyle, 'dashed');
export default s;
:root {
--border-style--none: none;
--border-style--solid: solid;
--border-style--dashed: dashed;
--border-style--dotted: dotted;
--border-style--double: double;
--border-style--groove: groove;
--border-style--inset: inset;
--border-style--outset: outset;
--border-style: dashed;
}
Extending the Default Border Style Values
You can customize which style is used as the default while keeping all other standard styles. Use the @
prefix to reference another key in the values object:
import { styleframe } from 'styleframe';
import { useBorderStyle, defaultBorderStyleValues } from '@styleframe/theme';
const s = styleframe();
const { borderStyle } = useBorderStyle(s, {
...defaultBorderStyleValues,
default: '@dashed'
});
export default s;
:root {
--border-style--none: none;
--border-style--solid: solid;
--border-style--dashed: dashed;
--border-style--dotted: dotted;
--border-style--double: double;
--border-style--groove: groove;
--border-style--inset: inset;
--border-style--outset: outset;
--border-style: var(--border-style--dashed);
}
--border-style
variable makes it easy to change your entire application's border style by overriding just one variable, while still having specific style variants available when needed.useBorderWidth
The useBorderWidth()
function creates a set of border width variables for common border thickness levels.
import { styleframe } from 'styleframe';
import { useBorderWidth } from '@styleframe/theme';
const s = styleframe();
const {
borderWidth,
borderWidthNone,
borderWidthThin,
borderWidthMedium,
borderWidthThick,
} = useBorderWidth(s);
export default s;
:root {
--border-width--none: 0;
--border-width--thin: thin;
--border-width--medium: medium;
--border-width--thick: thick;
--border-width: var(--border-width--thin);
}
The function creates variables for standard CSS border widths including none
(0), thin
, medium
, and thick
. The default --border-width
variable references thin
by default.
thin
, medium
, and thick
as keyword values that browsers interpret consistently. Typically, thin
is 1px, medium
is 3px, and thick
is 5px, but this can vary slightly by browser.Creating Custom Border Width Variables
You can provide completely custom border width values with specific pixel measurements:
import { styleframe } from 'styleframe';
import { useBorderWidth } from '@styleframe/theme';
const s = styleframe();
const {
borderWidth,
borderWidthHairline,
borderWidthBold,
borderWidthHeavy
} = useBorderWidth(s, {
default: '1px',
hairline: '0.5px',
bold: '2px',
heavy: '4px'
});
export default s;
:root {
--border-width--hairline: 0.5px;
--border-width--bold: 2px;
--border-width--heavy: 4px;
--border-width: 1px;
}
Updating the Default Border Width Variable
You can override the default border width value after creating it by using variable()
:
import { styleframe } from 'styleframe';
import { useBorderWidth } from '@styleframe/theme';
const s = styleframe();
const { variable } = s;
const { borderWidth } = useBorderWidth(s);
// Override the default border width
variable(borderWidth, '2px');
export default s;
:root {
--border-width--none: 0;
--border-width--thin: thin;
--border-width--medium: medium;
--border-width--thick: thick;
--border-width: 2px;
}
Extending the Default Border Width Values
You can customize which width is used as the default while keeping all other standard widths. Use the @
prefix to reference another key in the values object:
import { styleframe } from 'styleframe';
import { useBorderWidth, defaultBorderWidthValues } from '@styleframe/theme';
const s = styleframe();
const { borderWidth } = useBorderWidth(s, {
...defaultBorderWidthValues,
default: '@medium'
});
export default s;
:root {
--border-width--none: 0;
--border-width--thin: thin;
--border-width--medium: medium;
--border-width--thick: thick;
--border-width: var(--border-width--medium);
}
defaultBorderWidthValues
and adding your own custom widths. This gives you the flexibility of both approaches.useBorderColor
The useBorderColor()
function creates a set of border color variables that reference your base color system. This provides semantic separation between general colors and colors specifically intended for borders.
import { styleframe } from 'styleframe';
import { useColor, useBorderColor } from '@styleframe/theme';
const s = styleframe();
const { ref } = s;
// Define base colors
const { colorGray, colorPrimary, colorSuccess, colorDanger } = useColor(s, {
gray: '#6b7280',
primary: '#3b82f6',
success: '#10b981',
danger: '#ef4444',
} as const);
// Create border color variables
const {
borderColor,
borderColorPrimary,
borderColorSuccess,
borderColorDanger,
} = useBorderColor(s, {
default: ref(colorGray),
primary: ref(colorPrimary),
success: ref(colorSuccess),
danger: ref(colorDanger),
} as const);
export default s;
:root {
--color--gray: oklch(0.5575 0.0165 244.89 / 1);
--color--primary: oklch(0.6109 0.1903 263.71 / 1);
--color--success: oklch(0.7051 0.1654 165.47 / 1);
--color--danger: oklch(0.6278 0.2158 27.33 / 1);
--border-color: var(--color--gray);
--border-color--primary: var(--color--primary);
--border-color--success: var(--color--success);
--border-color--danger: var(--color--danger);
}
Each key in the object becomes a border color variable with the prefix border-color--
, and the export name is automatically converted to camelCase (e.g., default
→ borderColor
, primary
→ borderColorPrimary
).
useBorderColor()
instead of directly referencing color variables makes your intent explicit and allows you to easily adjust border colors independently from your base color palette. For example, you might want borders to be lighter or more desaturated than the base colors.Creating Border Color Lightness Variants
You can combine useBorderColor()
with color manipulation composables to create sophisticated border color systems:
import { styleframe } from 'styleframe';
import { useColor, useColorLightness, useBorderColor } from '@styleframe/theme';
const s = styleframe();
const { ref } = s;
// Base color
const { colorGray } = useColor(s, { gray: '#6b7280' } as const);
// Create lightness variants
const { colorGray300, colorGray500, colorGray700 } = useColorLightness(s, colorGray, {
300: 35,
500: 55,
700: 82,
} as const);
// Create semantic border colors
const {
borderColor,
borderColorLighter,
borderColorDarker,
} = useBorderColor(s, {
default: ref(colorGray500),
lighter: ref(colorGray300),
darker: ref(colorGray700),
} as const);
export default s;
:root {
--color--gray: oklch(0.5575 0.0165 244.89 / 1);
--color--gray-300: oklch(from var(--color--gray) 0.35 c h / a);
--color--gray-500: oklch(from var(--color--gray) 0.55 c h / a);
--color--gray-700: oklch(from var(--color--gray) 0.82 c h / a);
--border-color: var(--color--gray-500);
--border-color--lighter: var(--color--gray-300);
--border-color--darker: var(--color--gray-700);
}
Creating Border Color State Variants
Create border colors for different interactive states:
import { styleframe } from 'styleframe';
import { useBorderStyle, useBorderWidth, useColor, useBorderColor } from '@styleframe/theme';
const s = styleframe();
const { ref, selector, css } = s;
const { borderStyle } = useBorderStyle(s);
const { borderWidth } = useBorderWidth(s);
// Define base colors for different states
const { colorGray, colorBlue, colorRed, colorGreen } = useColor(s, {
gray: '#d1d5db',
blue: '#3b82f6',
red: '#ef4444',
green: '#10b981',
} as const);
// Create state-based border colors
const {
borderColor,
borderColorHover,
borderColorFocus,
borderColorError,
borderColorSuccess,
} = useBorderColor(s, {
default: ref(colorGray),
hover: ref(colorBlue),
focus: ref(colorBlue),
error: ref(colorRed),
success: ref(colorGreen),
} as const);
// Apply state-based borders
selector('.input', {
border: css`${ref(borderWidth)} ${ref(borderStyle)} ${ref(borderColor)}`,
transition: 'border-color 0.2s ease',
'&:hover': {
borderColor: ref(borderColorHover),
},
'&:focus': {
borderColor: ref(borderColorFocus),
outline: 'none',
},
'&.error': {
borderColor: ref(borderColorError),
},
'&.success': {
borderColor: ref(borderColorSuccess),
},
});
export default s;
Updating the Default Border Color Variable in Themes
Create borders that automatically adapt to light and dark themes:
import { styleframe } from 'styleframe';
import { useBorderStyle, useBorderWidth, useColor, useColorLightness, useBorderColor } from '@styleframe/theme';
const s = styleframe();
const { ref, selector, css } = s;
const { borderStyle } = useBorderStyle(s);
const { borderWidth } = useBorderWidth(s);
// Base color
const { colorGray } = useColor(s, { gray: '#6b7280' } as const);
// Create lightness variants
const { colorGray300, colorGray700 } = useColorLightness(s, colorGray, {
300: 35,
700: 82,
} as const);
const { borderColor } = useBorderColor(s, colorGray, {
default: ref(colorGray300),
} as const);
theme('dark', (ctx) => {
// Override default border color in dark theme
ctx.variable(borderColor, ref(colorGray700));
});
// Use theme-aware border
selector('.card', {
border: css`${ref(borderWidth)} ${ref(borderStyle)} ${ref(borderColor)}`,
});
export default s;
:root {
--border-style--none: none;
--border-style--solid: solid;
--border-style--dashed: dashed;
--border-style--dotted: dotted;
--border-style--double: double;
--border-style--groove: groove;
--border-style--inset: inset;
--border-style--outset: outset;
--border-style: var(--border-style--solid);
--border-width--none: 0;
--border-width--thin: thin;
--border-width--medium: medium;
--border-width--thick: thick;
--border-width: var(--border-width--thin);
--color--gray: oklch(0.5575 0.0165 244.89 / 1);
--color--gray-300: oklch(from var(--color--gray) 0.82 c h / a);
--color--gray-700: oklch(from var(--color--gray) 0.35 c h / a);
--border-color: var(--color--gray-300);
}
[data-theme="dark"] {
--border-color: var(--color--gray-700);
}
.card {
border: var(--border-width) var(--border-style) var(--border-color);
}
Using Border Variables
Here's how to combine all three border composables to create a comprehensive border system:
import { styleframe } from 'styleframe';
import { useBorderStyle, useBorderWidth, useColor, useBorderColor } from '@styleframe/theme';
const s = styleframe();
const { ref, selector, css } = s;
// Setup border styles and widths
const { borderStyle, borderStyleDashed } = useBorderStyle(s);
const { borderWidth, borderWidthMedium } = useBorderWidth(s);
// Setup base colors
const { colorGray, colorPrimary, colorSuccess, colorWarning, colorDanger } = useColor(s, {
gray: '#d1d5db',
primary: '#3b82f6',
success: '#10b981',
warning: '#f59e0b',
danger: '#ef4444',
} as const);
// Setup border colors
const {
borderColor,
borderColorPrimary,
borderColorSuccess,
borderColorWarning,
borderColorDanger,
} = useBorderColor(s, {
default: ref(colorGray),
primary: ref(colorPrimary),
success: ref(colorSuccess),
warning: ref(colorWarning),
danger: ref(colorDanger),
} as const);
// Use border variables in components
selector('.card', {
border: css`${ref(borderWidth)} ${ref(borderStyle)} ${ref(borderColor)}`,
});
selector('.input', {
border: css`${ref(borderWidth)} ${ref(borderStyle)} ${ref(borderColor)}`,
'&:focus': {
borderColor: ref(borderColorPrimary),
},
});
selector('.alert', {
border: css`${ref(borderWidth)} ${ref(borderStyle)} ${ref(borderColor)}`,
'&.success': {
borderColor: ref(borderColorSuccess),
borderLeftWidth: ref(borderWidthMedium),
},
'&.warning': {
borderColor: ref(borderColorWarning),
borderLeftWidth: ref(borderWidthMedium),
},
'&.danger': {
borderColor: ref(borderColorDanger),
borderLeftWidth: ref(borderWidthMedium),
},
});
export default s;
:root {
--border-style--none: none;
--border-style--solid: solid;
--border-style--dashed: dashed;
--border-style--dotted: dotted;
--border-style--double: double;
--border-style--groove: groove;
--border-style--inset: inset;
--border-style--outset: outset;
--border-style: var(--border-style--solid);
--border-width--none: 0;
--border-width--thin: thin;
--border-width--medium: medium;
--border-width--thick: thick;
--border-width: var(--border-width--thin);
--color--gray: oklch(0.8485 0.0061 264.07 / 1);
--color--primary: oklch(0.6109 0.1903 263.71 / 1);
--color--success: oklch(0.7051 0.1654 165.47 / 1);
--color--warning: oklch(0.7444 0.1609 60.67 / 1);
--color--danger: oklch(0.6278 0.2158 27.33 / 1);
--border-color: var(--color--gray);
--border-color--primary: var(--color--primary);
--border-color--success: var(--color--success);
--border-color--warning: var(--color--warning);
--border-color--danger: var(--color--danger);
}
.card {
border: var(--border-width) var(--border-style) var(--border-color);
}
.input {
border: var(--border-width) var(--border-style) var(--border-color);
&:focus {
border-color: var(--border-color--primary);
}
}
.alert {
border: var(--border-width) var(--border-style) var(--border-color);
&.success {
border-color: var(--border-color--success);
border-left-width: var(--border-width--medium);
}
&.warning {
border-color: var(--border-color--warning);
border-left-width: var(--border-width--medium);
}
&.danger {
border-color: var(--border-color--danger);
border-left-width: var(--border-width--medium);
}
}
Best Practices
- Use
useBorderColor()
for semantic separation: Create border-specific color variables instead of directly referencing color variables. This gives you flexibility to adjust border colors independently. - Leverage default variables: Use the
--border-style
,--border-width
, and--border-color
variables for consistency across your application. - Create complete border variables: Combine width, style, and color into single variables (like
--border
) for frequently used border combinations. - Consider transparency: Use alpha channel adjustments on border colors for subtle borders that work on any background.
- Keep it simple: Most applications only need 2-3 border widths and primarily use
solid
anddashed
styles. - Test accessibility: Ensure borders provide sufficient contrast and aren't the only indicator of interactive states.
- Plan for themes: Use
useBorderColor()
together withtheme()
to create theme-aware border colors that automatically adapt to light/dark modes.
useBorderColor()
composable accepts any value, not just color references. You can pass direct color values, but using references from useColor()
is recommended for consistency and maintainability.FAQ
useBorderColor()
creates semantic separation between general colors and border colors. This allows you to adjust border colors independently (e.g., making them lighter, more desaturated, or transparent) without affecting your base color palette. It also makes your intent explicit when reading the code.solid
(for most borders), dashed
(for secondary or inactive states), and none
(to remove borders).thin
, medium
, thick
) are good defaults and are consistently rendered across browsers. Use pixel values when you need exact control, especially for hairline borders (0.5px
or 1px
) or when your design system requires specific measurements.borderTop
for top-only borders, or use border: none
as a base and then override specific sides. You can also use the --border-width--none
variable to effectively hide borders without changing the style or color.--border-width--medium
) combined with a high-contrast color. Consider using outline
instead of border
for focus states to avoid layout shifts, or ensure your components have space allocated for the border change.useBorderColor()
with composables like useColorLightness()
, useColorShade()
, and useColorTint()
to create sophisticated border color systems with automatic variants. Just pass the manipulated color references to useBorderColor()
.