or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

ai-advanced.mddata-display.mdform-components.mdindex.mdinput-selection.mdlayout-navigation.mdloading-progress.mdmodal-overlay.mdtheming-customization.mdtiles-layout.md
tile.json

theming-customization.mddocs/

Theming & Customization

Theme provider, CSS customization, and utility hooks for adapting Carbon components to your brand and design requirements.

Capabilities

Theme Provider

Global theme management with support for Carbon's built-in themes and custom theme creation.

/**
 * Global theme provider for Carbon components
 */
interface ThemeProps {
  /** Theme variant to apply */
  theme?: "white" | "g10" | "g90" | "g100" | string;
  /** Components to theme */
  children: React.ReactNode;
  /** CSS class name */
  className?: string;
}

/**
 * Global theme provider with extended options
 */
interface GlobalThemeProps {
  /** Theme variant */
  theme?: "white" | "g10" | "g90" | "g100" | string;
  /** Global theme children */
  children: React.ReactNode;
}

Usage Examples:

import { Theme, GlobalTheme } from "@carbon/react";

// Basic theme provider
<Theme theme="g90">
  <App />
</Theme>

// Global theme (affects entire document)
<GlobalTheme theme="g10">
  <App />
</GlobalTheme>

// Theme switching
function ThemeSwitcher() {
  const [currentTheme, setCurrentTheme] = useState("white");
  
  return (
    <Theme theme={currentTheme}>
      <div>
        <select 
          value={currentTheme} 
          onChange={(e) => setCurrentTheme(e.target.value)}
        >
          <option value="white">White (Light)</option>
          <option value="g10">Gray 10 (Light)</option>
          <option value="g90">Gray 90 (Dark)</option>
          <option value="g100">Gray 100 (Dark)</option>
        </select>
        <MyApplication />
      </div>
    </Theme>
  );
}

// Custom theme integration
<Theme theme="custom-brand-theme">
  <App />
</Theme>

Layer Context

Layer context system for proper nested component styling and visual hierarchy.

/**
 * Layer context provider for nested styling
 */
interface LayerProps {
  /** Layer children */
  children: React.ReactNode;
  /** Layer level (0-2) for visual hierarchy */
  level?: 0 | 1 | 2;
  /** CSS class name */
  className?: string;
}

Usage Examples:

import { Layer, Tile, Button } from "@carbon/react";

// Layer hierarchy for visual depth
<div>
  {/* Base layer (level 0) */}
  <Tile>
    <h2>Main Content</h2>
    <p>This is the base layer content.</p>
    
    <Layer level={1}>
      {/* First nested layer */}
      <Tile>
        <h3>Nested Panel</h3>
        <p>This content is on layer 1.</p>
        
        <Layer level={2}>
          {/* Second nested layer */}
          <Tile>
            <h4>Deep Nested Content</h4>
            <p>This is the deepest nested layer (level 2).</p>
            <Button>Action</Button>
          </Tile>
        </Layer>
      </Tile>
    </Layer>
  </Tile>
</div>

// Modal with proper layering
<Modal open={isOpen}>
  <Layer>
    <h2>Modal Title</h2>
    <p>Modal content on proper layer.</p>
    <Layer level={1}>
      <Tile>Nested content in modal</Tile>
    </Layer>
  </Layer>
</Modal>

Prefix & ID Management

Utility hooks and components for managing CSS class prefixes and unique IDs.

/**
 * CSS class prefix hook
 * @returns Current CSS class prefix
 */
function usePrefix(): string;

/**
 * ID prefix hook for unique identifier generation
 * @returns Current ID prefix
 */
function useIdPrefix(): string;

/**
 * Class prefix provider
 */
interface ClassPrefixProps {
  /** CSS class prefix */
  prefix: string;
  /** Children to apply prefix to */
  children: React.ReactNode;
}

/**
 * ID prefix provider
 */
interface IdPrefixProps {
  /** ID prefix */
  prefix: string;
  /** Children to apply prefix to */
  children: React.ReactNode;
}

Usage Examples:

import { 
  usePrefix, 
  useIdPrefix, 
  ClassPrefix, 
  IdPrefix 
} from "@carbon/react";

// Using prefix hooks in custom components
function MyCustomComponent() {
  const prefix = usePrefix();
  const idPrefix = useIdPrefix();
  
  return (
    <div className={`${prefix}--my-component`}>
      <input id={`${idPrefix}--my-input`} />
      <label htmlFor={`${idPrefix}--my-input`}>Custom Input</label>
    </div>
  );
}

// Custom prefix providers
<ClassPrefix prefix="my-brand">
  <IdPrefix prefix="my-app">
    <App />
  </IdPrefix>
</ClassPrefix>

// Using hooks to maintain consistency
function CustomCard({ title, children }) {
  const prefix = usePrefix();
  const idPrefix = useIdPrefix();
  const titleId = `${idPrefix}--card-title-${Math.random()}`;
  
  return (
    <div 
      className={`${prefix}--card`}
      aria-labelledby={titleId}
    >
      <h3 id={titleId} className={`${prefix}--card__title`}>
        {title}
      </h3>
      <div className={`${prefix}--card__content`}>
        {children}
      </div>
    </div>
  );
}

SCSS Integration

Sass/SCSS integration patterns and CSS variable usage for advanced styling.

/**
 * SCSS import pattern for Carbon styles
 */
interface CarbonSCSSImports {
  /** Import all Carbon styles */
  all: "@carbon/react/scss/index.scss";
  /** Import specific component styles */
  component: "@carbon/react/scss/components/[component]/index.scss";
  /** Import theme tokens */
  tokens: "@carbon/styles/scss/theme";
  /** Import layout utilities */
  layout: "@carbon/styles/scss/layout";
  /** Import motion tokens */
  motion: "@carbon/styles/scss/motion";
  /** Import spacing tokens */
  spacing: "@carbon/styles/scss/spacing";
  /** Import type utilities */
  type: "@carbon/styles/scss/type";
}

SCSS Usage Examples:

// Import all Carbon styles
@use "@carbon/react/scss/index.scss";

// Import specific components
@use "@carbon/react/scss/components/button";
@use "@carbon/react/scss/components/text-input";

// Import design tokens
@use "@carbon/styles/scss/theme" as *;
@use "@carbon/styles/scss/spacing" as *;
@use "@carbon/styles/scss/type" as *;

// Custom component with Carbon tokens
.my-custom-component {
  background: $background;
  color: $text-primary;
  padding: $spacing-05;
  border-radius: $spacing-02;
  
  // Use Carbon type styles
  @include type-style('body-02');
  
  &:hover {
    background: $background-hover;
  }
}

// Theme-aware custom styles
.my-themed-component {
  // Light theme styles
  @include theme($theme: $white) {
    border: 1px solid $border-subtle-01;
  }
  
  // Dark theme styles  
  @include theme($theme: $g90) {
    border: 1px solid $border-subtle-01;
  }
}

// Responsive design with Carbon breakpoints
@use "@carbon/styles/scss/breakpoint" as *;

.responsive-component {
  padding: $spacing-03;
  
  @include breakpoint(md) {
    padding: $spacing-05;
  }
  
  @include breakpoint(lg) {
    padding: $spacing-07;
  }
}

CSS Custom Properties

CSS custom property usage for runtime theme customization.

/* CSS Custom Properties for theming */
.custom-theme {
  /* Override Carbon color tokens */
  --cds-background: #f8f9fa;
  --cds-text-primary: #1a1a1a;
  --cds-interactive-01: #0066cc;
  --cds-interactive-02: #6c757d;
  
  /* Override spacing tokens */
  --cds-spacing-05: 1rem;
  --cds-spacing-06: 1.5rem;
  
  /* Override typography tokens */
  --cds-productive-heading-03-font-size: 1.25rem;
  --cds-productive-heading-03-line-height: 1.4;
}

/* Component-specific theming */
.custom-button-theme {
  --cds-button-primary: #ff6b35;
  --cds-button-primary-hover: #e55a2b;
  --cds-button-primary-active: #cc4e24;
}

CSS Custom Property Usage:

import { Button, TextInput } from "@carbon/react";

// Apply custom theme via CSS classes
<div className="custom-theme">
  <Button kind="primary">Custom themed button</Button>
  <TextInput labelText="Custom themed input" />
</div>

// Inline CSS custom properties
<div 
  style={{
    '--cds-interactive-01': '#ff6b35',
    '--cds-interactive-02': '#6c757d'
  }}
>
  <Button kind="primary">Inline themed button</Button>
</div>

Theme Tokens Access

Accessing Carbon design tokens programmatically for custom styling.

/**
 * Carbon theme tokens (available as CSS custom properties)
 */
interface CarbonTokens {
  // Color tokens
  background: string;
  "text-primary": string;
  "text-secondary": string;
  "interactive-01": string;
  "interactive-02": string;
  "interactive-03": string;
  "interactive-04": string;
  
  // Spacing tokens  
  "spacing-01": string; // 0.125rem
  "spacing-02": string; // 0.25rem
  "spacing-03": string; // 0.5rem
  "spacing-04": string; // 0.75rem
  "spacing-05": string; // 1rem
  "spacing-06": string; // 1.5rem
  "spacing-07": string; // 2rem
  "spacing-08": string; // 2.5rem
  "spacing-09": string; // 3rem
  "spacing-10": string; // 4rem
  "spacing-11": string; // 5rem
  "spacing-12": string; // 6rem
  
  // Layout tokens
  "layout-01": string; // 1rem
  "layout-02": string; // 1.5rem
  "layout-03": string; // 2rem
  "layout-04": string; // 3rem
  "layout-05": string; // 4rem
  "layout-06": string; // 6rem
  "layout-07": string; // 10rem
}

Accessing tokens in React:

// Using CSS custom properties in inline styles
function CustomComponent() {
  return (
    <div
      style={{
        backgroundColor: 'var(--cds-background)',
        color: 'var(--cds-text-primary)',
        padding: 'var(--cds-spacing-05)',
        borderRadius: 'var(--cds-spacing-02)',
        border: '1px solid var(--cds-border-subtle-01)'
      }}
    >
      Custom styled component using Carbon tokens
    </div>
  );
}

// Using getComputedStyle to access tokens
function useThemeToken(tokenName: string) {
  const [value, setValue] = useState('');
  
  useEffect(() => {
    const root = document.documentElement;
    const computedValue = getComputedStyle(root)
      .getPropertyValue(`--cds-${tokenName}`)
      .trim();
    setValue(computedValue);
  }, [tokenName]);
  
  return value;
}

// Using the hook
function ThemedComponent() {
  const primaryColor = useThemeToken('interactive-01');
  const spacing = useThemeToken('spacing-05');
  
  return (
    <div style={{ color: primaryColor, padding: spacing }}>
      Dynamically themed content
    </div>
  );
}

Breakpoint Integration

Responsive design utilities and breakpoint management.

/**
 * Carbon breakpoints
 */
interface CarbonBreakpoints {
  sm: "320px";    // Small
  md: "672px";    // Medium  
  lg: "1056px";   // Large
  xlg: "1312px";  // Extra large
  max: "1584px";  // Maximum
}

/**
 * Media query hook for responsive behavior
 */
function useMatchMedia(query: string): boolean;

Breakpoint Usage:

import { useMatchMedia } from "@carbon/react";

// Responsive component behavior
function ResponsiveComponent() {
  const isMobile = useMatchMedia('(max-width: 671px)');
  const isTablet = useMatchMedia('(min-width: 672px) and (max-width: 1055px)');
  const isDesktop = useMatchMedia('(min-width: 1056px)');
  
  return (
    <div>
      {isMobile && <MobileLayout />}
      {isTablet && <TabletLayout />}
      {isDesktop && <DesktopLayout />}
    </div>
  );
}

// Responsive grid
function ResponsiveGrid() {
  const isMobile = useMatchMedia('(max-width: 671px)');
  
  return (
    <Grid>
      <Row>
        <Column sm={4} md={8} lg={12}>
          <h1>Responsive Content</h1>
        </Column>
      </Row>
      <Row>
        {isMobile ? (
          // Mobile: Stack vertically
          <>
            <Column sm={4}><Card>Card 1</Card></Column>
            <Column sm={4}><Card>Card 2</Card></Column>
            <Column sm={4}><Card>Card 3</Card></Column>
          </>
        ) : (
          // Desktop: Side by side
          <>
            <Column md={2} lg={4}><Card>Card 1</Card></Column>
            <Column md={3} lg={4}><Card>Card 2</Card></Column>
            <Column md={3} lg={4}><Card>Card 3</Card></Column>
          </>
        )}
      </Row>
    </Grid>
  );
}

Motion & Animation

Motion token integration for consistent animations and transitions.

/**
 * Carbon motion tokens
 */
interface CarbonMotion {
  "duration-fast-01": "70ms";
  "duration-fast-02": "110ms";
  "duration-moderate-01": "150ms";
  "duration-moderate-02": "240ms";
  "duration-slow-01": "400ms";
  "duration-slow-02": "700ms";
  
  "easing-standard": "cubic-bezier(0.2, 0, 0.38, 0.9)";
  "easing-entrance": "cubic-bezier(0, 0, 0.38, 0.9)";
  "easing-exit": "cubic-bezier(0.2, 0, 1, 0.9)";
}

Motion Usage:

/* Using motion tokens in CSS */
.custom-animation {
  transition: 
    opacity var(--cds-duration-moderate-01) var(--cds-easing-standard),
    transform var(--cds-duration-moderate-01) var(--cds-easing-standard);
}

.fade-in {
  animation: fadeIn var(--cds-duration-moderate-02) var(--cds-easing-entrance);
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(1rem); }
  to { opacity: 1; transform: translateY(0); }
}
// Using motion tokens in React
function AnimatedComponent({ isVisible }) {
  return (
    <div
      style={{
        opacity: isVisible ? 1 : 0,
        transform: isVisible ? 'translateY(0)' : 'translateY(1rem)',
        transition: `
          opacity var(--cds-duration-moderate-01) var(--cds-easing-standard),
          transform var(--cds-duration-moderate-01) var(--cds-easing-standard)
        `
      }}
    >
      Animated content
    </div>
  );
}

Typography Integration

/**
 * Carbon type styles available via SCSS mixins
 */
interface CarbonTypeStyles {
  // Productive styles
  "productive-heading-01": { fontSize: "14px", lineHeight: "18px" };
  "productive-heading-02": { fontSize: "16px", lineHeight: "22px" };
  "productive-heading-03": { fontSize: "20px", lineHeight: "26px" };
  "productive-heading-04": { fontSize: "28px", lineHeight: "36px" };
  "productive-heading-05": { fontSize: "32px", lineHeight: "40px" };
  "productive-heading-06": { fontSize: "42px", lineHeight: "50px" };
  "productive-heading-07": { fontSize: "54px", lineHeight: "64px" };
  
  // Body styles
  "body-01": { fontSize: "14px", lineHeight: "18px" };
  "body-02": { fontSize: "16px", lineHeight: "22px" };
  
  // Code styles
  "code-01": { fontSize: "12px", lineHeight: "16px" };
  "code-02": { fontSize: "14px", lineHeight: "20px" };
  
  // Helper styles
  "helper-text-01": { fontSize: "12px", lineHeight: "16px" };
  "helper-text-02": { fontSize: "14px", lineHeight: "18px" };
  
  // Label styles
  "label-01": { fontSize: "12px", lineHeight: "16px" };
  "label-02": { fontSize: "14px", lineHeight: "18px" };
}