JavaScript and React course Β· Module 7: Styling and Animations
Building Your Own Design Systems - Visual Architecture of the Cosmic Future
In this lesson6
On our intergalactic journey through the world of React, we have arrived at one of the most important aspects of building advanced interfaces - design systems. If component frameworks are like ready-made space stations, then design systems are like blueprints for building entire civilizations - consistent, functional, and scalable.
What is a Design System?
A design system is a collection of standards, components, and guidelines that define how user interfaces are created across an entire application or product ecosystem. It is like a building code for space stations and vehicles - ensuring that all elements fit together, creating a consistent, functional, and aesthetic whole.
A good design system includes:
- Design principles - fundamental values and design philosophy
- Component system - a reusable UI element library
- Design tokens - standardized values for colors, typography, spacing, etc.
- Interaction patterns - standard ways for users to interact with the interface
- Documentation - instructions and guidelines for the team
Why Create Your Own Design System?
Creating your own design system may seem like a big challenge, but it brings numerous benefits:
- Consistency - all interface elements speak the same visual language
- Scalability - easier application development and adding new features
- Efficiency - faster design and implementation thanks to ready components
- Better collaboration - shared language and standards for designers and developers
- Brand identity - a unique and recognizable user experience
Building a Design System from Scratch
Building your own design system is like terraforming a new planet - it requires vision, planning, and a systematic approach. Let's go through the key steps of this process.
1. Defining Design Principles
Start with fundamental principles that will guide the entire system. These are like the constitution of your cosmic civilization. Example principles:
- Simplicity - Eliminate complexity whenever possible
- Consistency - Solve similar problems in similar ways
- Accessibility - The interface must be usable by everyone
- Performance - Optimize for speed and responsiveness
- Inclusivity - Design with diverse needs in mind
2. Creating Design Tokens
Design tokens are variables that store basic stylistic values. They are like chemical elements from which your world is built.
Colors
1// design-tokens.js
2
3export const colors = {
4 // Primary palette
5 primary: {
6 50: '#e3f2fd',
7 100: '#bbdefb',
8 200: '#90caf9',
9 300: '#64b5f6',
10 400: '#42a5f5',
11 500: '#2196f3', // Main color
12 600: '#1e88e5',
13 700: '#1976d2',
14 800: '#1565c0',
15 900: '#0d47a1',
16 },
17
18 // Semantic colors
19 success: '#4caf50',
20 warning: '#ff9800',
21 error: '#f44336',
22 info: '#2196f3',
23
24 // Neutrals
25 neutral: {
26 50: '#fafafa',
27 100: '#f5f5f5',
28 200: '#eeeeee',
29 300: '#e0e0e0',
30 400: '#bdbdbd',
31 500: '#9e9e9e',
32 600: '#757575',
33 700: '#616161',
34 800: '#424242',
35 900: '#212121',
36 },
37};Typography
1// design-tokens.js
2
3export const typography = {
4 fontFamily: {
5 base: '"Inter", system-ui, -apple-system, sans-serif',
6 heading: '"Space Grotesk", sans-serif',
7 monospace: '"Fira Code", monospace',
8 },
9
10 fontSize: {
11 xs: '0.75rem', // 12px
12 sm: '0.875rem', // 14px
13 base: '1rem', // 16px
14 lg: '1.125rem', // 18px
15 xl: '1.25rem', // 20px
16 '2xl': '1.5rem', // 24px
17 '3xl': '1.875rem', // 30px
18 '4xl': '2.25rem', // 36px
19 '5xl': '3rem', // 48px
20 },
21
22 fontWeight: {
23 light: 300,
24 regular: 400,
25 medium: 500,
26 semibold: 600,
27 bold: 700,
28 },
29
30 lineHeight: {
31 none: 1,
32 tight: 1.25,
33 snug: 1.375,
34 normal: 1.5,
35 relaxed: 1.625,
36 loose: 2,
37 },
38};Spacing
1// design-tokens.js
2
3export const spacing = {
4 px: '1px',
5 0: '0',
6 0.5: '0.125rem', // 2px
7 1: '0.25rem', // 4px
8 1.5: '0.375rem', // 6px
9 2: '0.5rem', // 8px
10 2.5: '0.625rem', // 10px
11 3: '0.75rem', // 12px
12 3.5: '0.875rem', // 14px
13 4: '1rem', // 16px
14 5: '1.25rem', // 20px
15 6: '1.5rem', // 24px
16 8: '2rem', // 32px
17 10: '2.5rem', // 40px
18 12: '3rem', // 48px
19 16: '4rem', // 64px
20 20: '5rem', // 80px
21 24: '6rem', // 96px
22 32: '8rem', // 128px
23 40: '10rem', // 160px
24 48: '12rem', // 192px
25 56: '14rem', // 224px
26 64: '16rem', // 256px
27};Shadows
1// design-tokens.js
2
3export const shadows = {
4 sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
5 md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
6 lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
7 xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
8 '2xl': '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
9 inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)',
10 outline: '0 0 0 3px rgba(66, 153, 225, 0.5)',
11 none: 'none',
12};Breakpoints
1// design-tokens.js
2
3export const breakpoints = {
4 xs: '0px',
5 sm: '600px',
6 md: '960px',
7 lg: '1280px',
8 xl: '1920px',
9};3. Building Atomic Components
Now let's move on to building basic, atomic components that will form the fundamental blocks of your system. This is like creating the first buildings on a new planet.
Button
1import React from 'react';
2import styled, { css } from 'styled-components';
3import { colors, typography, spacing } from '../design-tokens';
4
5// Define button variants
6const variants = {
7 primary: css`
8 background-color: ${colors.primary[500]};
9 color: white;
10
11 &:hover {
12 background-color: ${colors.primary[600]};
13 }
14
15 &:focus {
16 box-shadow: 0 0 0 3px ${colors.primary[300]};
17 }
18 `,
19
20 secondary: css`
21 background-color: white;
22 color: ${colors.primary[500]};
23 border: 1px solid ${colors.primary[500]};
24
25 &:hover {
26 background-color: ${colors.primary[50]};
27 }
28
29 &:focus {
30 box-shadow: 0 0 0 3px ${colors.primary[300]};
31 }
32 `,
33
34 danger: css`
35 background-color: ${colors.error};
36 color: white;
37
38 &:hover {
39 background-color: #d32f2f;
40 }
41
42 &:focus {
43 box-shadow: 0 0 0 3px rgba(244, 67, 54, 0.4);
44 }
45 `,
46};
47
48// Define button sizes
49const sizes = {
50 small: css`
51 padding: ${spacing[1]} ${spacing[2]};
52 font-size: ${typography.fontSize.sm};
53 `,
54
55 medium: css`
56 padding: ${spacing[2]} ${spacing[4]};
57 font-size: ${typography.fontSize.base};
58 `,
59
60 large: css`
61 padding: ${spacing[3]} ${spacing[6]};
62 font-size: ${typography.fontSize.lg};
63 `,
64};
65
66// Button component
67const StyledButton = styled.button`
68 font-family: ${typography.fontFamily.base};
69 font-weight: ${typography.fontWeight.medium};
70 border-radius: 4px;
71 border: none;
72 cursor: pointer;
73 transition: all 0.2s ease-in-out;
74 display: inline-flex;
75 align-items: center;
76 justify-content: center;
77
78 &:disabled {
79 opacity: 0.6;
80 cursor: not-allowed;
81 }
82
83 /* Apply variant */
84 ${props => variants[props.variant || 'primary']}
85
86 /* Apply size */
87 ${props => sizes[props.size || 'medium']}
88`;
89
90const Button = ({ children, variant, size, ...rest }) => (
91 <StyledButton variant={variant} size={size} {...rest}>
92 {children}
93 </StyledButton>
94);
95
96export default Button;Card
1import React from 'react';
2import styled from 'styled-components';
3import { spacing, shadows, colors } from '../design-tokens';
4
5const StyledCard = styled.div`
6 background-color: white;
7 border-radius: 8px;
8 box-shadow: ${shadows.md};
9 overflow: hidden;
10`;
11
12const CardHeader = styled.div`
13 padding: ${spacing[4]} ${spacing[4]} ${spacing[2]};
14 border-bottom: 1px solid ${colors.neutral[200]};
15`;
16
17const CardTitle = styled.h3`
18 margin: 0;
19 font-size: ${props => props.theme.typography.fontSize.xl};
20 font-weight: ${props => props.theme.typography.fontWeight.semibold};
21`;
22
23const CardSubtitle = styled.div`
24 color: ${colors.neutral[600]};
25 margin-top: ${spacing[1]};
26`;
27
28const CardContent = styled.div`
29 padding: ${spacing[4]};
30`;
31
32const CardFooter = styled.div`
33 padding: ${spacing[2]} ${spacing[4]};
34 background-color: ${colors.neutral[50]};
35 border-top: 1px solid ${colors.neutral[200]};
36 display: flex;
37 justify-content: flex-end;
38`;
39
40const Card = ({ children, ...rest }) => {
41 return <StyledCard {...rest}>{children}</StyledCard>;
42};
43
44Card.Header = CardHeader;
45Card.Title = CardTitle;
46Card.Subtitle = CardSubtitle;
47Card.Content = CardContent;
48Card.Footer = CardFooter;
49
50export default Card;4. Building Complex Components
Once you have basic components, you can start building more complex interface elements, using atomic components as building blocks. This is like creating advanced structures after building the foundations.
Dialog
1import React, { useEffect, useRef } from 'react';
2import styled from 'styled-components';
3import Button from './Button';
4import { colors, spacing, shadows } from '../design-tokens';
5
6const Overlay = styled.div`
7 position: fixed;
8 top: 0;
9 left: 0;
10 right: 0;
11 bottom: 0;
12 background-color: rgba(0, 0, 0, 0.5);
13 display: flex;
14 align-items: center;
15 justify-content: center;
16 z-index: 1000;
17`;
18
19const DialogContainer = styled.div`
20 background-color: white;
21 border-radius: 8px;
22 box-shadow: ${shadows.xl};
23 width: 100%;
24 max-width: ${props =>
25 props.size === 'small' ? '400px' : props.size === 'large' ? '800px' : '600px'};
26 max-height: 90vh;
27 overflow-y: auto;
28 display: flex;
29 flex-direction: column;
30`;
31
32const DialogHeader = styled.div`
33 padding: ${spacing[4]};
34 border-bottom: 1px solid ${colors.neutral[200]};
35 display: flex;
36 align-items: center;
37 justify-content: space-between;
38`;
39
40const DialogTitle = styled.h3`
41 margin: 0;
42 font-weight: 600;
43`;
44
45const CloseButton = styled.button`
46 background: none;
47 border: none;
48 cursor: pointer;
49 color: ${colors.neutral[500]};
50 font-size: 1.5rem;
51 line-height: 1;
52
53 &:hover {
54 color: ${colors.neutral[700]};
55 }
56`;
57
58const DialogContent = styled.div`
59 padding: ${spacing[4]};
60 flex: 1;
61`;
62
63const DialogFooter = styled.div`
64 padding: ${spacing[3]} ${spacing[4]};
65 border-top: 1px solid ${colors.neutral[200]};
66 display: flex;
67 justify-content: flex-end;
68 gap: ${spacing[2]};
69`;
70
71const Dialog = ({
72 isOpen,
73 onClose,
74 title,
75 children,
76 size = 'medium',
77 confirmText = 'Confirm',
78 cancelText = 'Cancel',
79 onConfirm,
80 hideActions = false
81}) => {
82 const dialogRef = useRef(null);
83
84 // Close dialog on ESC key press
85 useEffect(() => {
86 const handleKeyDown = (e) => {
87 if (e.key === 'Escape') {
88 onClose();
89 }
90 };
91
92 if (isOpen) {
93 window.addEventListener('keydown', handleKeyDown);
94 }
95
96 return () => {
97 window.removeEventListener('keydown', handleKeyDown);
98 };
99 }, [isOpen, onClose]);
100
101 // Stop click propagation inside dialog
102 const handleDialogClick = (e) => {
103 e.stopPropagation();
104 };
105
106 if (!isOpen) return null;
107
108 return (
109 <Overlay onClick={onClose}>
110 <DialogContainer size={size} onClick={handleDialogClick} ref={dialogRef}>
111 <DialogHeader>
112 <DialogTitle>{title}</DialogTitle>
113 <CloseButton onClick={onClose}>×</CloseButton>
114 </DialogHeader>
115 <DialogContent>{children}</DialogContent>
116 {!hideActions && (
117 <DialogFooter>
118 <Button variant="secondary" onClick={onClose}>
119 {cancelText}
120 </Button>
121 {onConfirm && (
122 <Button variant="primary" onClick={onConfirm}>
123 {confirmText}
124 </Button>
125 )}
126 </DialogFooter>
127 )}
128 </DialogContainer>
129 </Overlay>
130 );
131};
132
133export default Dialog;5. Implementing Interaction Patterns
Interaction patterns are standard ways in which users interact with the interface. Here is an example of implementing the "Notification" (Toast) pattern:
1import React, { useState, useEffect } from 'react';
2import styled, { css, keyframes } from 'styled-components';
3import { colors, spacing, shadows } from '../design-tokens';
4
5// Entry animation
6const slideIn = keyframes`
7 from {
8 transform: translateX(100%);
9 opacity: 0;
10 }
11 to {
12 transform: translateX(0);
13 opacity: 1;
14 }
15`;
16
17// Exit animation
18const slideOut = keyframes`
19 from {
20 transform: translateX(0);
21 opacity: 1;
22 }
23 to {
24 transform: translateX(100%);
25 opacity: 0;
26 }
27`;
28
29// Different notification types
30const toastTypes = {
31 success: css`
32 background-color: ${colors.success};
33 color: white;
34 `,
35 error: css`
36 background-color: ${colors.error};
37 color: white;
38 `,
39 warning: css`
40 background-color: ${colors.warning};
41 color: white;
42 `,
43 info: css`
44 background-color: ${colors.info};
45 color: white;
46 `,
47};
48
49const ToastContainer = styled.div`
50 position: fixed;
51 top: ${spacing[4]};
52 right: ${spacing[4]};
53 z-index: 1500;
54 display: flex;
55 flex-direction: column;
56 gap: ${spacing[2]};
57`;
58
59const Toast = styled.div`
60 min-width: 300px;
61 max-width: 400px;
62 padding: ${spacing[3]};
63 border-radius: 4px;
64 box-shadow: ${shadows.lg};
65 display: flex;
66 align-items: center;
67 justify-content: space-between;
68 animation: ${props => (props.leaving ? slideOut : slideIn)} 0.3s ease-in-out forwards;
69
70 ${props => toastTypes[props.type || 'info']}
71`;
72
73const CloseButton = styled.button`
74 background: none;
75 border: none;
76 color: inherit;
77 opacity: 0.7;
78 cursor: pointer;
79 font-size: 1.25rem;
80
81 &:hover {
82 opacity: 1;
83 }
84`;
85
86// Context for notifications
87const ToastContext = React.createContext();
88
89// Notification provider
90export const ToastProvider = ({ children }) => {
91 const [toasts, setToasts] = useState([]);
92
93 const addToast = (message, type = 'info', duration = 5000) => {
94 const id = Date.now();
95 setToasts(prev => [...prev, { id, message, type, duration }]);
96
97 if (duration !== Infinity) {
98 setTimeout(() => removeToast(id), duration);
99 }
100
101 return id;
102 };
103
104 const removeToast = (id) => {
105 setToasts(prev => {
106 const toast = prev.find(t => t.id === id);
107 if (toast) {
108 // Mark the toast as leaving
109 return prev.map(t => t.id === id ? { ...t, leaving: true } : t);
110 }
111 return prev;
112 });
113
114 // Remove toast after animation ends
115 setTimeout(() => {
116 setToasts(prev => prev.filter(t => t.id !== id));
117 }, 300); // Animation duration
118 };
119
120 return (
121 <ToastContext.Provider value={{ addToast, removeToast }}>
122 {children}
123 <ToastContainer>
124 {toasts.map(toast => (
125 <Toast key={toast.id} type={toast.type} leaving={toast.leaving}>
126 <span>{toast.message}</span>
127 <CloseButton onClick={() => removeToast(toast.id)}>×</CloseButton>
128 </Toast>
129 ))}
130 </ToastContainer>
131 </ToastContext.Provider>
132 );
133};
134
135// Hook for using notifications
136export const useToast = () => {
137 const context = React.useContext(ToastContext);
138 if (!context) {
139 throw new Error('useToast must be used within a ToastProvider');
140 }
141 return context;
142};
143
144// Usage example:
145// const { addToast } = useToast();
146// addToast('Operation completed successfully!', 'success');6. Design System Documentation
The last but no less important step is creating documentation that allows other developers and designers to use your design system. Popular tools for creating documentation include:
- Storybook - interactive component documentation
- Docusaurus - documentation site generator
- Docz - a tool dedicated to React component documentation
Example of a component configuration in Storybook:
1// Button.stories.js
2
3import React from 'react';
4import Button from './Button';
5
6export default {
7 title: 'Atoms/Button',
8 component: Button,
9 argTypes: {
10 variant: {
11 control: { type: 'select', options: ['primary', 'secondary', 'danger'] },
12 description: 'Button variant determining its style and purpose',
13 table: {
14 type: { summary: 'string' },
15 defaultValue: { summary: 'primary' },
16 },
17 },
18 size: {
19 control: { type: 'select', options: ['small', 'medium', 'large'] },
20 description: 'Button size',
21 table: {
22 type: { summary: 'string' },
23 defaultValue: { summary: 'medium' },
24 },
25 },
26 disabled: {
27 control: 'boolean',
28 description: 'Whether the button is disabled',
29 table: {
30 type: { summary: 'boolean' },
31 defaultValue: { summary: false },
32 },
33 },
34 },
35};
36
37// Basic usage
38export const Primary = () => <Button variant="primary">Primary button</Button>;
39
40// Variants
41export const Variants = () => (
42 <div style={{ display: 'flex', gap: '8px' }}>
43 <Button variant="primary">Primary</Button>
44 <Button variant="secondary">Secondary</Button>
45 <Button variant="danger">Danger</Button>
46 </div>
47);
48
49// Sizes
50export const Sizes = () => (
51 <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
52 <Button size="small">Small</Button>
53 <Button size="medium">Medium</Button>
54 <Button size="large">Large</Button>
55 </div>
56);
57
58// Disabled state
59export const Disabled = () => <Button disabled>Disabled button</Button>;Design System Implementation Strategies
Implementing a design system in an existing project is like modernizing a working space station - it requires planning and a gradual approach:
- Incremental implementation - start with small, isolated components
- Parallel development - first build the system in a separate branch or project
- Page-by-page migration - migrate one page or section at a time
- Component by component - replace old components with new ones one by one
Best Practices for Creating Design Systems
- Start with research - analyze competitors and proven patterns
- Collaborate from the start - involve designers and developers
- Think systemically - design a system, not individual screens
- Test early and often - gather feedback from users
- Document everything - not just code, but also decisions and justifications
- Evolve, don't revolutionize - a design system should be a living, evolving entity
Practical Exercise: Create a Micro Design System for a Space Application
Design and implement a small design system for a cosmic mission monitoring application. Your system should contain:
- Design tokens - define basic variables for colors, typography, and spacing
- Atomic components - create at least 3 basic components (e.g., Button, Card, Input)
- One complex component - use atomic components to create a more complex component (e.g., Dashboard Card, Mission Status)
- Documentation - describe your system and components
Remember that a good design system is not just about aesthetics, but above all about functionality, consistency, and ease of use. Create the system with future application development in mind.
When you create your own design system, you become not only a programmer but also an architect of the cosmic future of interfaces. Like the creators of the first colonies on other planets, you are building the foundations upon which future generations of developers and users will build.
Code for this lesson: App.jsx
1import React, { useState } from 'react';
2import styled, { ThemeProvider, createGlobalStyle } from 'styled-components';
3
4// ===== DESIGN TOKENS =====
5// Design tokens are variables storing base style values
6const tokens = {
7 colors: {
8 primary: '#6c5ce7',
9 primaryLight: '#a29bfe',
10 secondary: '#00cec9',
11 success: '#00b894',
12 warning: '#fdcb6e',
13 danger: '#d63031',
14 text: '#2d3436',
15 textLight: '#636e72',
16 background: '#f8f9fa',
17 surface: '#ffffff',
18 border: '#dfe6e9',
19 },
20 spacing: {
21 xs: '4px',
22 sm: '8px',
23 md: '16px',
24 lg: '24px',
25 xl: '32px',
26 xxl: '48px',
27 },
28 radius: {
29 sm: '4px',
30 md: '8px',
31 lg: '16px',
32 full: '9999px',
33 },
34 fontSize: {
35 xs: '0.75rem',
36 sm: '0.875rem',
37 base: '1rem',
38 lg: '1.125rem',
39 xl: '1.5rem',
40 xxl: '2rem',
41 },
42 fontWeight: {
43 normal: 400,
44 medium: 500,
45 semibold: 600,
46 bold: 700,
47 },
48 shadows: {
49 sm: '0 1px 3px rgba(0,0,0,0.12)',
50 md: '0 4px 6px rgba(0,0,0,0.1)',
51 lg: '0 10px 25px rgba(0,0,0,0.15)',
52 },
53};
54
55// ===== GLOBAL STYLES =====
56const GlobalStyle = createGlobalStyle`
57 * { margin: 0; padding: 0; box-sizing: border-box; }
58 body {
59 font-family: 'Segoe UI', system-ui, sans-serif;
60 background: ${tokens.colors.background};
61 color: ${tokens.colors.text};
62 }
63`;
64
65// ===== COMPONENT: BUTTON =====
66// Button with variants: primary, secondary, danger
67const Button = styled.button`
68 display: inline-flex;
69 align-items: center;
70 justify-content: center;
71 padding: ${tokens.spacing.sm} ${tokens.spacing.lg};
72 font-size: ${tokens.fontSize.sm};
73 font-weight: ${tokens.fontWeight.semibold};
74 border-radius: ${tokens.radius.md};
75 border: 2px solid transparent;
76 cursor: pointer;
77 transition: all 0.2s ease;
78
79 background: ${p => {
80 if (p.variant === 'secondary') return 'transparent';
81 if (p.variant === 'danger') return tokens.colors.danger;
82 return tokens.colors.primary;
83 }};
84
85 color: ${p => {
86 if (p.variant === 'secondary') return tokens.colors.primary;
87 return '#ffffff';
88 }};
89
90 border-color: ${p => {
91 if (p.variant === 'secondary') return tokens.colors.primary;
92 return 'transparent';
93 }};
94
95 &:hover {
96 transform: translateY(-2px);
97 box-shadow: ${tokens.shadows.md};
98 opacity: 0.9;
99 }
100
101 &:disabled {
102 opacity: 0.5;
103 cursor: not-allowed;
104 transform: none;
105 }
106`;
107
108// ===== COMPONENT: CARD =====
109const Card = styled.div`
110 background: ${tokens.colors.surface};
111 border-radius: ${tokens.radius.lg};
112 padding: ${tokens.spacing.lg};
113 box-shadow: ${tokens.shadows.sm};
114 border: 1px solid ${tokens.colors.border};
115 transition: all 0.3s ease;
116
117 &:hover {
118 box-shadow: ${tokens.shadows.lg};
119 transform: translateY(-4px);
120 }
121`;
122
123// ===== COMPONENT: INPUT =====
124const Input = styled.input`
125 width: 100%;
126 padding: ${tokens.spacing.sm} ${tokens.spacing.md};
127 font-size: ${tokens.fontSize.base};
128 border: 2px solid ${tokens.colors.border};
129 border-radius: ${tokens.radius.md};
130 background: ${tokens.colors.surface};
131 color: ${tokens.colors.text};
132 transition: border-color 0.2s ease;
133 outline: none;
134
135 &:focus {
136 border-color: ${tokens.colors.primary};
137 box-shadow: 0 0 0 3px ${tokens.colors.primaryLight}40;
138 }
139
140 &::placeholder {
141 color: ${tokens.colors.textLight};
142 }
143`;
144
145// TODO: Create a Badge component with variants
146// Badge should display small status labels
147// Warianty: success, warning, danger
148// Use tokens from the tokens object
149const Badge = styled.span`
150 display: inline-block;
151 padding: 2px ${tokens.spacing.sm};
152 font-size: ${tokens.fontSize.xs};
153 font-weight: ${tokens.fontWeight.bold};
154 border-radius: ${tokens.radius.full};
155 /* TODO: Add a dynamic background color based on props.variant */
156 /* Use tokens.colors[props.variant] with 20% opacity */
157 /* e.g. success -> green background, warning -> yellow background */
158 background: ${p => (tokens.colors[p.variant] || tokens.colors.primary) + '33'};
159 color: ${p => tokens.colors[p.variant] || tokens.colors.primary};
160`;
161
162// TODO: Create an Alert component for displaying messages
163// Alert should have variants: success, warning, danger
164// Use spacing, colors, and radius tokens
165const Alert = styled.div`
166 padding: ${tokens.spacing.md};
167 border-radius: ${tokens.radius.md};
168 margin-bottom: ${tokens.spacing.md};
169 border-left: 4px solid ${p => tokens.colors[p.variant] || tokens.colors.primary};
170 /* TODO: Add a background with slight transparency of the variant color */
171 background: ${p => (tokens.colors[p.variant] || tokens.colors.primary) + '15'};
172 color: ${tokens.colors.text};
173`;
174
175// ===== LAYOUT =====
176const Container = styled.div`
177 max-width: 700px;
178 margin: 0 auto;
179 padding: ${tokens.spacing.xl};
180`;
181
182const Title = styled.h1`
183 font-size: ${tokens.fontSize.xxl};
184 font-weight: ${tokens.fontWeight.bold};
185 color: ${tokens.colors.primary};
186 text-align: center;
187 margin-bottom: ${tokens.spacing.xs};
188`;
189
190const Subtitle = styled.p`
191 text-align: center;
192 color: ${tokens.colors.textLight};
193 margin-bottom: ${tokens.spacing.xl};
194`;
195
196const Section = styled.div`
197 margin-bottom: ${tokens.spacing.xl};
198`;
199
200const SectionTitle = styled.h2`
201 font-size: ${tokens.fontSize.lg};
202 margin-bottom: ${tokens.spacing.md};
203 color: ${tokens.colors.text};
204`;
205
206const ButtonGroup = styled.div`
207 display: flex;
208 gap: ${tokens.spacing.sm};
209 flex-wrap: wrap;
210`;
211
212const Grid = styled.div`
213 display: grid;
214 grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
215 gap: ${tokens.spacing.md};
216`;
217
218// ===== APPLICATION =====
219function DesignSystemDemo() {
220 const [inputValue, setInputValue] = useState('');
221
222 return (
223 <>
224 <GlobalStyle />
225 <Container>
226 <Title>Space Design System</Title>
227 <Subtitle>A consistent set of UI components</Subtitle>
228
229 {/* Section: Alerts */}
230 <Section>
231 <SectionTitle>Alerts</SectionTitle>
232 <Alert variant="success">Mission completed successfully!</Alert>
233 <Alert variant="warning">Low fuel level</Alert>
234 <Alert variant="danger">Gravitational anomaly detected</Alert>
235 </Section>
236
237 {/* Section: Buttons */}
238 <Section>
239 <SectionTitle>Buttons</SectionTitle>
240 <ButtonGroup>
241 <Button>Primary</Button>
242 <Button variant="secondary">Secondary</Button>
243 <Button variant="danger">Danger</Button>
244 <Button disabled>Disabled</Button>
245 </ButtonGroup>
246 </Section>
247
248 {/* Section: Badge */}
249 <Section>
250 <SectionTitle>Badge</SectionTitle>
251 <ButtonGroup>
252 <Badge variant="success">Online</Badge>
253 <Badge variant="warning">Standby</Badge>
254 <Badge variant="danger">Offline</Badge>
255 <Badge>Default</Badge>
256 </ButtonGroup>
257 </Section>
258
259 {/* Section: Input */}
260 <Section>
261 <SectionTitle>Input</SectionTitle>
262 <Input
263 placeholder="Enter target coordinates..."
264 value={inputValue}
265 onChange={e => setInputValue(e.target.value)}
266 />
267 </Section>
268
269 {/* Section: Cards */}
270 <Section>
271 <SectionTitle>Cards</SectionTitle>
272 <Grid>
273 <Card>
274 <h3 style={{ marginBottom: 8 }}>Navigation</h3>
275 <p style={{ color: tokens.colors.textLight, fontSize: 14, marginBottom: 12 }}>
276 Space navigation system
277 </p>
278 <Badge variant="success">Active</Badge>
279 </Card>
280 <Card>
281 <h3 style={{ marginBottom: 8 }}>Engines</h3>
282 <p style={{ color: tokens.colors.textLight, fontSize: 14, marginBottom: 12 }}>
283 Warp drive propulsion
284 </p>
285 <Badge variant="warning">Standby</Badge>
286 </Card>
287 <Card>
288 <h3 style={{ marginBottom: 8 }}>Shields</h3>
289 <p style={{ color: tokens.colors.textLight, fontSize: 14, marginBottom: 12 }}>
290 Electromagnetic shields
291 </p>
292 <Badge variant="danger">Offline</Badge>
293 </Card>
294 </Grid>
295 </Section>
296 </Container>
297 </>
298 );
299}
300
301export default DesignSystemDemo;Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What is the foundation (base) of every design system?
2. Which browser tool lets you see the final (computed) styles applied to an HTML element?
These are 2 of 3 questions for this lesson. Solve the rest in the game.