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: