Component Styling
Component styling system provides a clean, declarative way to create reusable UI components with consistent styling. It separates static base styles from dynamic styles and supports multiple themes.
Overview
The component system enables you to:
- Define reusable UI component styles with a declarative API
- Separate static styles from dynamic styles that depend on props
- Apply different themes to components based on state or context
- Handle additional attributes like ARIA properties or data attributes
API Reference
Component Configuration
Property | Type | Description |
---|---|---|
base |
Object | Static core styles that don't change based on props |
setup(props) |
Function | Function that returns dynamic styles based on props |
attrs(props) |
Function | Function that returns additional HTML attributes |
theme |
Object | Theme variations with their own class and style definitions |
Component Instance Result
Property | Type | Description |
---|---|---|
class |
String | Combined CSS classes from base and setup |
style |
String | Combined inline styles from base and setup |
attrs |
Object | Additional HTML attributes from the attrs function |
theme(name) |
Function | Function to apply a specific theme |
Basic Example
// Define a button component
const Button = xkin.component({
// Static base styles
base: {
class: "btn",
style: "display: inline-block; padding: 8px 16px; border-radius: 4px; cursor: pointer;",
},
// Dynamic styles based on props
setup: ({ size, disabled, fullWidth }) => ({
class: {
"btn-sm": size === "small",
"btn-lg": size === "large",
"btn-disabled": disabled,
"btn-block": fullWidth,
},
style: disabled ? "opacity: 0.6; pointer-events: none;" : "",
}),
// Additional HTML attributes
attrs: (props) => ({
type: props.type || "button",
"aria-disabled": props.disabled ? "true" : "false",
"data-size": props.size || "default",
}),
// Theme variations
theme: {
primary: {
class: "color-bg-primary color-tx-white",
style: "border: none;",
},
secondary: {
class: "color-bg-secondary color-tx-white",
style: "border: none;",
},
outline: {
class: "color-tx-primary",
style: "background: transparent; border: 1px solid currentColor;",
},
},
});
// Use the component with specific props
const submitButton = Button({
size: "large",
disabled: false,
fullWidth: true,
type: "submit",
});
console.log(submitButton);
// {
// class: "btn btn-lg btn-block",
// style: "display: inline-block; padding: 8px 16px; border-radius: 4px; cursor: pointer;",
// attrs: {
// type: "submit",
// "aria-disabled": "false",
// "data-size": "large"
// },
// theme: [Function]
// }
// Apply a theme
const primaryButton = submitButton.theme("primary");
console.log(primaryButton);
// {
// class: "btn btn-lg btn-block color-bg-primary color-tx-white",
// style: "display: inline-block; padding: 8px 16px; border-radius: 4px; cursor: pointer; border: none;"
// }
Dynamic Property Examples
Conditional Classes
const Card = xkin.component({
base: {
class: "card",
style: "border-radius: 8px; overflow: hidden;",
},
setup: ({ elevated, interactive, padding = "normal" }) => ({
class: {
"card-elevated": elevated,
"card-interactive": interactive,
"card-padding-sm": padding === "small",
"card-padding-lg": padding === "large",
},
style: elevated ? "box-shadow: 0 4px 8px rgba(0,0,0,0.1);" : "",
}),
});
Dynamic Styles
const Box = xkin.component({
base: {
class: "box",
style: "display: block;",
},
setup: ({ width, height, margin, padding, color }) => ({
style: {
width: width,
height: height,
margin: margin,
padding: padding,
backgroundColor: color,
},
}),
});
const redBox = Box({
width: "200px",
height: "100px",
padding: "16px",
color: "#f44336",
});
Integration with DOM
Use component styles with DOM elements:
// Define the component
const Input = xkin.component({
base: {
class: "input",
style: "padding: 8px; border: 1px solid #ccc; border-radius: 4px;",
},
setup: ({ invalid, disabled, size }) => ({
class: {
"input-invalid": invalid,
"input-disabled": disabled,
"input-sm": size === "small",
"input-lg": size === "large",
},
style: invalid ? "border-color: #dc3545;" : "",
}),
attrs: (props) => ({
type: props.type || "text",
disabled: props.disabled ? true : undefined,
"aria-invalid": props.invalid ? "true" : "false",
}),
});
// Apply to a DOM element
document.addEventListener("DOMContentLoaded", () => {
const inputElement = document.getElementById("username");
const inputControl = xkin.control(inputElement);
// Get component styles with specific props
const inputStyles = Input({
invalid: !inputElement.checkValidity(),
size: "large",
type: "email",
});
// Apply classes and styles
inputControl.add(inputStyles.class);
inputElement.style = inputStyles.style;
// Apply attributes
Object.entries(inputStyles.attrs).forEach(([key, value]) => {
if (value !== undefined) {
inputElement.setAttribute(key, value);
}
});
});
Performance Considerations
Component system uses memoization internally to optimize performance. The setup
and attrs
functions are only re-executed when their input props change.
For even better performance with frequently updating components, consider using xkin.memoizeOne
for your custom setup functions: