Skip to main content

TWConfig Package (@schwab/twconfig)

Overview

The @schwab/twconfig package provides shared Tailwind CSS v4 configuration themes that ensure consistent design system implementation across all applications in the Charles Schwab monorepo. It includes base themes, component-specific configurations, and responsive design utilities.

Architecture

Theme Configurations

1. Base Theme Configuration

Base Theme (src/base/theme.css)
/**
* Core design system theme with shadcn/ui integration
*/
@import "tailwindcss";

@source '../../../ui/src';
@source '../../../transformer/src';

@plugin 'tailwindcss-animate';

@custom-variant dark (&:is(.dark *));

@theme inline {
/* Semantic Color System */
--color-border: hsl(var(--border));
--color-input: hsl(var(--input));
--color-ring: hsl(var(--ring));
--color-background: hsl(var(--background));
--color-foreground: hsl(var(--foreground));

--color-primary: hsl(var(--primary));
--color-primary-foreground: hsl(var(--primary-foreground));

--color-secondary: hsl(var(--secondary));
--color-secondary-foreground: hsl(var(--secondary-foreground));

--color-destructive: hsl(var(--destructive));
--color-destructive-foreground: hsl(var(--destructive-foreground));

--color-muted: hsl(var(--muted));
--color-muted-foreground: hsl(var(--muted-foreground));

--color-accent: hsl(var(--accent));
--color-accent-foreground: hsl(var(--accent-foreground));

--color-popover: hsl(var(--popover));
--color-popover-foreground: hsl(var(--popover-foreground));

--color-card: hsl(var(--card));
--color-card-foreground: hsl(var(--card-foreground));

/* Border Radius System */
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
}

2. Light Theme Variables

Light Theme Color Palette
:root {
/* Charles Schwab Brand Colors - Light Mode */
--background: 217.2 100% 100%; /* Pure white background */
--foreground: 217.2 5% 10%; /* Near black text */
--card: 217.2 50% 98%; /* Subtle card background */
--card-foreground: 217.2 5% 15%; /* Card text color */
--popover: 217.2 100% 95%; /* Popover background */
--popover-foreground: 217.2 100% 10%; /* Popover text */

/* Primary Brand Blue */
--primary: 217.2 91.2% 59.8%; /* Schwab signature blue */
--primary-foreground: 0 0% 100%; /* White text on blue */

/* Secondary Colors */
--secondary: 217.2 30% 70%; /* Light blue gray */
--secondary-foreground: 0 0% 0%; /* Black text on secondary */

/* Muted/Accent Colors */
--muted: 179.2 30% 85%; /* Very light blue-green */
--muted-foreground: 217.2 5% 35%; /* Muted text color */
--accent: 179.2 30% 80%; /* Accent blue-green */
--accent-foreground: 217.2 5% 15%; /* Accent text color */

/* Utility Colors */
--destructive: 0 100% 30%; /* Error red */
--destructive-foreground: 217.2 5% 90%; /* Light text on red */
--border: 217.2 30% 85%; /* Border color */
--input: 217.2 30% 85%; /* Input border color */
--ring: 217.2 91.2% 59.8%; /* Focus ring color */
--radius: 0.5rem; /* Default border radius */
}

3. Dark Theme Variables

Dark Theme Color Palette
.dark {
/* Charles Schwab Brand Colors - Dark Mode */
--background: 217.2 50% 10%; /* Dark blue background */
--foreground: 217.2 5% 90%; /* Light text */
--card: 217.2 50% 15%; /* Dark card background */
--card-foreground: 217.2 5% 90%; /* Light card text */
--popover: 217.2 50% 5%; /* Very dark popover */
--popover-foreground: 217.2 5% 90%; /* Light popover text */

/* Primary remains consistent with brand */
--primary: 217.2 91.2% 59.8%; /* Same Schwab blue */
--primary-foreground: 0 0% 100%; /* White text on blue */

/* Adjusted secondary for dark mode */
--secondary: 217.2 30% 20%; /* Darker secondary */
--secondary-foreground: 0 0% 100%; /* White text */

/* Dark mode muted colors */
--muted: 179.2 30% 25%; /* Dark blue-green */
--muted-foreground: 217.2 5% 60%; /* Lighter muted text */
--accent: 179.2 30% 25%; /* Dark accent */
--accent-foreground: 217.2 5% 90%; /* Light accent text */

/* Dark mode utilities */
--destructive: 0 100% 30%; /* Consistent error red */
--destructive-foreground: 217.2 5% 90%; /* Light text on red */
--border: 217.2 30% 26%; /* Dark border */
--input: 217.2 30% 26%; /* Dark input border */
--ring: 217.2 91.2% 59.8%; /* Same focus ring */
}

Prospect Theme Configuration

1. Enhanced Typography and Layout

Prospect Theme (src/prospect/theme.css)
@import '../base/theme.css';

:root {
/* Container System for Marketing Pages */
--container-width-default: 1232px;
--container-width-wide: 1440px;
--container-width-xwide: 1600px;
}

@theme {
/* Enhanced Spacing System */
--spacing: 0.25rem; /* 4px base unit */

/* Responsive Breakpoint System */
--breakpoint-xxs: 0; /* 0px - Mobile first hack */
--breakpoint-xs: 20rem; /* 320px - Small phones */
--breakpoint-sm: 30rem; /* 480px - Large phones */
--breakpoint-md: 48rem; /* 768px - Tablets */
--breakpoint-lg: 64rem; /* 1024px - Small desktop */
--breakpoint-xl: 77rem; /* 1232px - Desktop */
--breakpoint-2xl: 90rem; /* 1440px - Large desktop */
--breakpoint-3xl: 100rem; /* 1600px - Extra large */

/* Container Utilities */
--container-7xl: 77rem; /* 1232px */
--container-8xl: 90rem; /* 1440px */
--container-9xl: 100rem; /* 1600px */
--container-story: 840px; /* Story templates */

/* Charles Modern Typography Scale */
--font-sans: "Charles Modern", sans-serif;
--font-serif: Georgia, serif;
--font-mono: Menlo, monospace;
--font-icon: "Schwab-Icon-Font", sans-serif;

/* Precise Typography Scale */
--text-4xs: 10px; /* Fine print */
--text-3xs: 12px; /* Legal text */
--text-2xs: 13px; /* Small labels */
--text-xs: 14px; /* Form labels */
--text-sm: 15px; /* Secondary text */
--text-base: 17px; /* Body text */
--text-lg: 18px; /* Large body */
--text-xl: 19px; /* Subheadings */
--text-2xl: 20px; /* Small headings */
--text-3xl: 24px; /* Medium headings */
--text-4xl: 32px; /* Large headings */
--text-5xl: 34px; /* Hero subhead */
--text-6xl: 36px; /* Hero heading */
--text-7xl: 40px; /* Display heading */
--text-8xl: 48px; /* Large display */
--text-9xl: 56px; /* Extra large */
}

2. Extended Color Palette

Prospect Color Extensions
@theme {
/* Schwab Brand Color Extensions */
--color-schwab-blue-50: hsl(217.2 91.2% 95%);
--color-schwab-blue-100: hsl(217.2 91.2% 90%);
--color-schwab-blue-200: hsl(217.2 91.2% 80%);
--color-schwab-blue-300: hsl(217.2 91.2% 70%);
--color-schwab-blue-400: hsl(217.2 91.2% 60%);
--color-schwab-blue-500: hsl(217.2 91.2% 50%);
--color-schwab-blue-600: hsl(217.2 91.2% 40%);
--color-schwab-blue-700: hsl(217.2 91.2% 30%);
--color-schwab-blue-800: hsl(217.2 91.2% 20%);
--color-schwab-blue-900: hsl(217.2 91.2% 10%);

/* Semantic Marketing Colors */
--color-success: hsl(142.1 76.2% 36.3%);
--color-success-foreground: hsl(355.7 100% 97.3%);
--color-warning: hsl(32.1 94.6% 43.7%);
--color-warning-foreground: hsl(355.7 100% 97.3%);
--color-info: hsl(221.2 83.2% 53.3%);
--color-info-foreground: hsl(355.7 100% 97.3%);

/* Gradient Support */
--gradient-primary: linear-gradient(135deg,
hsl(var(--primary)) 0%,
hsl(217.2 91.2% 45%) 100%);
--gradient-secondary: linear-gradient(135deg,
hsl(var(--secondary)) 0%,
hsl(179.2 30% 60%) 100%);
}

Meganav Theme Configuration

1. Navigation-Specific Styling

Meganav Theme (src/meganav/theme.css)
@layer theme, base, components, utilities;
@import "./preflight.css" layer(base);
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/utilities.css" layer(utilities);

@source '../../../ui/src';
@plugin 'tailwindcss-animate';

@custom-variant dark (&:is(.dark *));

@theme inline {
/* Navigation-specific color system */
--color-nav-background: hsl(var(--nav-background));
--color-nav-foreground: hsl(var(--nav-foreground));
--color-nav-hover: hsl(var(--nav-hover));
--color-nav-active: hsl(var(--nav-active));
--color-nav-border: hsl(var(--nav-border));

/* Dropdown and mega menu colors */
--color-dropdown-background: hsl(var(--dropdown-background));
--color-dropdown-foreground: hsl(var(--dropdown-foreground));
--color-dropdown-shadow: hsl(var(--dropdown-shadow));

/* Interactive states */
--color-link-hover: hsl(var(--link-hover));
--color-link-active: hsl(var(--link-active));
--color-focus-ring: hsl(var(--focus-ring));
}

:root {
/* Meganav specific variables */
--nav-background: 217.2 100% 100%;
--nav-foreground: 217.2 5% 10%;
--nav-hover: 217.2 30% 95%;
--nav-active: 217.2 91.2% 59.8%;
--nav-border: 217.2 30% 85%;

--dropdown-background: 217.2 100% 100%;
--dropdown-foreground: 217.2 5% 10%;
--dropdown-shadow: 217.2 10% 90%;

--link-hover: 217.2 91.2% 50%;
--link-active: 217.2 91.2% 40%;
--focus-ring: 217.2 91.2% 59.8%;
}

Usage Examples

1. Application Integration

Next.js Application Configuration
// apps/www.schwab.com/tailwind.config.ts
import type { Config } from 'tailwindcss';

const config: Config = {
// Import the prospect theme for marketing site
content: [
'./src/**/*.{js,ts,jsx,tsx,mdx}',
'../../packages/ui/src/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {
// Additional customizations for specific app needs
animation: {
'fade-in': 'fadeIn 0.5s ease-in-out',
'slide-up': 'slideUp 0.3s ease-out',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(100%)' },
'100%': { transform: 'translateY(0)' },
},
},
},
},
plugins: [],
};

// In your CSS file:
// @import '@schwab/twconfig/prospect';

export default config;

2. Component Library Integration

UI Component with Theme Integration
// packages/ui/src/components/Button/Button.tsx
import { cva, type VariantProps } from 'class-variance-authority';
import { cn } from '@schwab/utilities';

const buttonVariants = cva(
// Base styles using design tokens
'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background',
{
variants: {
variant: {
// Primary uses brand colors from theme
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
// Secondary uses theme secondary colors
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
// Destructive uses semantic colors
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
// Outline variant with theme borders
outline: 'border border-input hover:bg-accent hover:text-accent-foreground',
// Ghost variant with subtle interactions
ghost: 'hover:bg-accent hover:text-accent-foreground',
// Link variant
link: 'underline-offset-4 hover:underline text-primary',
},
size: {
default: 'h-10 py-2 px-4',
sm: 'h-9 px-3 rounded-md',
lg: 'h-11 px-8 rounded-md',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);

export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean;
}

export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : 'button';
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
);
}
);

3. Responsive Design Implementation

Responsive Component with Theme Breakpoints
// Using the prospect theme breakpoint system
export function ResponsiveHero({ title, subtitle, image }: HeroProps) {
return (
<section className={cn(
// Container using theme container utilities
'container-9xl mx-auto px-4',
// Responsive grid using theme breakpoints
'grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-12',
// Responsive padding using theme spacing
'py-8 md:py-12 lg:py-16 xl:py-20'
)}>
<div className={cn(
// Responsive text alignment
'text-center lg:text-left',
// Order on different screen sizes
'order-2 lg:order-1'
)}>
<h1 className={cn(
// Typography scale from theme
'text-4xl md:text-6xl lg:text-7xl xl:text-8xl',
// Font family from theme
'font-sans font-bold',
// Colors from theme
'text-foreground',
// Responsive margins
'mb-4 md:mb-6 lg:mb-8'
)}>
{title}
</h1>

<p className={cn(
// Subtitle typography
'text-lg md:text-xl lg:text-2xl',
// Muted color from theme
'text-muted-foreground',
// Responsive margins
'mb-6 md:mb-8 lg:mb-10'
)}>
{subtitle}
</p>

<div className={cn(
// Button container
'flex flex-col sm:flex-row gap-4',
// Responsive alignment
'justify-center lg:justify-start'
)}>
<Button size="lg" className="text-base">
Get Started
</Button>
<Button variant="outline" size="lg" className="text-base">
Learn More
</Button>
</div>
</div>

<div className={cn(
// Image container
'relative order-1 lg:order-2',
// Responsive aspect ratios
'aspect-square sm:aspect-video lg:aspect-auto'
)}>
<img
src={image.src}
alt={image.alt}
className={cn(
'w-full h-full object-cover',
// Theme border radius
'rounded-lg'
)}
/>
</div>
</section>
);
}

4. Dark Mode Implementation

Theme-Aware Dark Mode Component
'use client';

import { useTheme } from 'next-themes';
import { Moon, Sun } from 'lucide-react';

export function ThemeToggle() {
const { theme, setTheme } = useTheme();

return (
<Button
variant="ghost"
size="icon"
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
className={cn(
// Theme-aware colors
'hover:bg-accent hover:text-accent-foreground',
// Focus ring from theme
'focus-visible:ring-ring'
)}
>
<Sun className={cn(
'h-[1.2rem] w-[1.2rem]',
// Light mode visibility
'rotate-0 scale-100 transition-all',
// Dark mode hidden
'dark:-rotate-90 dark:scale-0'
)} />
<Moon className={cn(
'absolute h-[1.2rem] w-[1.2rem]',
// Light mode hidden
'rotate-90 scale-0 transition-all',
// Dark mode visibility
'dark:rotate-0 dark:scale-100'
)} />
<span className="sr-only">Toggle theme</span>
</Button>
);
}

// Global CSS for theme switching
// This leverages the theme's CSS custom properties
.theme-transition {
transition-property: background-color, border-color, color, fill, stroke;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 200ms;
}

Advanced Features

1. CSS Custom Properties Integration

Advanced Custom Properties Usage
/* Leveraging theme custom properties for dynamic theming */
@theme {
/* Dynamic color generation */
--color-brand-50: color-mix(in hsl, var(--primary) 10%, white);
--color-brand-100: color-mix(in hsl, var(--primary) 20%, white);
--color-brand-200: color-mix(in hsl, var(--primary) 30%, white);
/* ... continue the scale */
--color-brand-900: color-mix(in hsl, var(--primary) 90%, black);

/* Contextual spacing based on container */
--spacing-contextual: clamp(1rem, 5vw, 3rem);

/* Fluid typography */
--text-fluid-base: clamp(1rem, 2.5vw, 1.125rem);
--text-fluid-lg: clamp(1.125rem, 3vw, 1.25rem);
--text-fluid-xl: clamp(1.25rem, 4vw, 1.5rem);

/* Dynamic shadows */
--shadow-dynamic:
0 1px 3px color-mix(in hsl, var(--foreground) 12%, transparent),
0 1px 2px color-mix(in hsl, var(--foreground) 24%, transparent);
}

2. Animation Integration

Theme-Aware Animations
@theme {
/* Animation durations following design system */
--animate-duration-fast: 150ms;
--animate-duration-normal: 300ms;
--animate-duration-slow: 500ms;

/* Easing functions */
--animate-ease-in: cubic-bezier(0.4, 0, 1, 1);
--animate-ease-out: cubic-bezier(0, 0, 0.2, 1);
--animate-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
}

/* Animation utilities */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

@keyframes slideUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}

@keyframes scaleIn {
from { transform: scale(0.95); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}

/* Theme-aware animation classes */
.animate-fade-in {
animation: fadeIn var(--animate-duration-normal) var(--animate-ease-out);
}

.animate-slide-up {
animation: slideUp var(--animate-duration-normal) var(--animate-ease-out);
}

.animate-scale-in {
animation: scaleIn var(--animate-duration-fast) var(--animate-ease-out);
}

The TWConfig package provides a comprehensive design system foundation that ensures visual consistency, accessibility, and maintainability across the Charles Schwab digital ecosystem while supporting modern CSS features and responsive design patterns.