Custom Properties and Math Functions Practice
This assignment gives you hands-on practice with CSS custom properties and math functions. You will build a small component system that uses custom properties for theming, implements fluid typography and spacing with clamp(), and creates component variations using scoped custom properties. The result will be a responsive, maintainable design system built entirely with modern native CSS.
By the end of this assignment, you will have practical experience creating flexible design systems that adapt to different contexts without media queries or preprocessors.
Assignment Overview
You will create a single HTML file with embedded CSS that demonstrates a complete design system built with custom properties and math functions. The page will include navigation, card components, buttons with state variations, and fluid typography that scales smoothly across viewport sizes.
All styling should be accomplished using custom properties defined in :root for global values and scoped to specific elements for variations. You will use clamp() for fluid sizing and calc() for derived values.
Starter Files
Download the starter HTML file below. It contains a basic page structure with navigation, hero section, card grid, and buttons. Your job is to style it entirely using custom properties and math functions.
The starter file includes the HTML structure but minimal CSS. You will add all custom properties, styling, and theming from scratch.
Assignment Instructions
1. Create Your Design System Foundation
Add a <style> section to your HTML file (or use an embedded style tag if one exists). Define a comprehensive set of custom properties in :root that will serve as your design system foundation.
Your design system should include:
- Color palette: Primary, secondary, background, text colors (use any colors you like)
-
Spacing scale: Define at least 5 spacing values (xs, sm, md, lg, xl) using
clamp()so they scale with viewport size -
Typography scale: Base font size using
clamp(), then derive larger/smaller sizes usingcalc() - Layout values: Max content width, border radius values, shadow values
:root {
/* Colors */
--color-primary: #3498db;
--color-secondary: #2ecc71;
--color-background: #ffffff;
--color-text: #2c3e50;
/* Fluid Spacing (scales with viewport) */
--spacing-xs: clamp(0.25rem, 1vw, 0.5rem);
--spacing-sm: clamp(0.5rem, 2vw, 1rem);
/* Add more spacing values... */
/* Fluid Typography */
--font-size-base: clamp(1rem, 2.5vw, 1.125rem);
--font-size-lg: calc(var(--font-size-base) * 1.25);
/* Add more type sizes... */
/* Layout */
--max-width: 1200px;
--border-radius: 0.5rem;
/* Add more layout values... */
}
Use clamp(min, preferred, max) for spacing and typography that scales smoothly. The preferred value should use viewport units (vw) so it grows with screen size, while min and max keep it readable on all devices.
2. Style the Navigation
Style the navigation component using your custom properties. The navigation should have a background color, padding using your spacing scale, and links that use your color system.
Requirements:
- Use custom properties for all colors and spacing
- Style navigation links with hover and active states
- Use flexbox for layout with gap using your spacing scale
- Apply smooth transitions to interactive elements
3. Implement Fluid Typography
Apply your typography scale to the page content. Headings should use larger sizes from your scale, body text should use the base size, and all sizes should scale fluidly with the viewport.
Requirements:
-
<h1>should use your largest fluid font size -
<h2>should use a mid-range size - Body text should use your base font size
- All typography should reference custom properties
4. Build Card Components with Variations
Style the card components using custom properties. Then create variations by scoping custom properties to modifier classes.
Requirements:
-
Base
.cardclass uses global custom properties - Add padding, border radius, and box shadow using your design system values
-
Create at least two card variations (e.g.,
.card-featured,.card-muted) by redefining scoped custom properties - Card variations should change colors or emphasis without duplicating CSS
.card {
--card-bg: var(--color-background);
--card-text: var(--color-text);
--card-border: var(--color-primary);
background: var(--card-bg);
color: var(--card-text);
border-left: 4px solid var(--card-border);
padding: var(--spacing-md);
border-radius: var(--border-radius);
/* More styles... */
}
.card-featured {
--card-bg: var(--color-primary);
--card-text: white;
--card-border: var(--color-secondary);
}
5. Create Button System with State Variations
Style buttons using custom properties and create hover/active states that adjust colors dynamically using color-mix() or relative color syntax.
Requirements:
- Base button uses custom properties for background and text colors
-
Hover state should darken the button (use
color-mix()with black) - Active state should darken further
-
Create at least one button variation (e.g.,
.btn-secondary) - Use transitions for smooth state changes
.btn {
--btn-bg: var(--color-primary);
--btn-text: white;
background: var(--btn-bg);
color: var(--btn-text);
padding: var(--spacing-sm) var(--spacing-md);
border: none;
border-radius: var(--border-radius);
cursor: pointer;
transition: background 0.2s;
}
.btn:hover {
background: color-mix(in oklch, var(--btn-bg) 85%, black);
}
.btn:active {
background: color-mix(in oklch, var(--btn-bg) 70%, black);
}
6. Add Dark Theme Support
Create a dark theme by redefining your color custom properties when a data-theme="dark" attribute is present on the root element.
Requirements:
- Define dark theme colors that override the default light theme
- All components should automatically adapt because they use custom properties
- Ensure sufficient contrast in both themes
[data-theme="dark"] {
--color-background: #1a1a1a;
--color-text: #f0f0f0;
--color-primary: #5dade2;
/* Redefine other colors for dark mode... */
}
Because your components reference custom properties, changing the theme values automatically updates the entire page. You do not need to modify individual component styles. This is the power of custom properties.
7. Add Reflection Comment
At the top of your CSS (inside the <style> tag), add a multiline comment reflecting on your experience with this assignment.
Address these points:
- What advantages did you find in using custom properties versus hard-coded values?
-
How did
clamp()make responsive design easier or harder? - What challenges did you encounter with scoped custom properties for component variations?
- How does the theme switching mechanism demonstrate the power of custom properties?
/*
* REFLECTION
*
* Custom Properties vs Hard-coded Values:
* [Your thoughts here...]
*
* Using clamp() for Fluid Design:
* [Your thoughts here...]
*
* Scoped Custom Properties:
* [Your thoughts here...]
*
* Theme Switching:
* [Your thoughts here...]
*/
Submission Requirements
Submit a single HTML file to Canvas with:
-
All CSS embedded in a
<style>tag - Multiline comment reflection at the top of your CSS
- Complete implementation of all requirements (navigation, typography, cards, buttons, dark theme)
- Properly formatted and indented code
Your file should be functional when opened in a modern browser. Test it by resizing the browser window to see your fluid typography and spacing in action.
Evaluation Criteria
Your assignment will be evaluated on:
- Design System Completeness: Comprehensive custom properties for colors, spacing, and typography
-
Fluid Sizing: Effective use of
clamp()andcalc()for responsive values - Component Variations: Proper use of scoped custom properties for cards and buttons
- Theme Implementation: Working dark theme that adapts all components
- Code Quality: Clean, well-organized CSS with consistent naming
- Reflection: Thoughtful analysis of your experience with custom properties and math functions
Tips for Success
Start with the Design System
Define all your custom properties first before writing any component styles. This forces you to think systematically about your design values and ensures consistency.
Test Fluid Sizing
Resize your browser window frequently to see how your clamp() values behave. Adjust the min, preferred, and max values until the scaling feels natural.
Use DevTools
Browser DevTools can show you the computed values of custom properties. This is invaluable for debugging and understanding how values cascade and resolve.
Keep Calculations Simple
When using calc() to derive values, keep expressions straightforward. Complex nested calculations are hard to debug and maintain.
Test Both Themes
To test your dark theme, manually add data-theme="dark" to the <html> tag in your HTML file. Remember to remove it before submission unless you want dark mode as the default.
Optional Enhancement: Theme Toggle
If you want to add interactive theme switching, here is a simple JavaScript snippet you can include at the bottom of your HTML file (before the closing </body> tag). This is completely optional and not required for the assignment.
<!-- Add a theme toggle button in your navigation -->
<button id="theme-toggle" class="btn">Toggle Theme</button>
<!-- Add this script before closing body tag -->
<script type="module">
const toggleButton = document.getElementById('theme-toggle');
const html = document.documentElement;
toggleButton.addEventListener('click', () => {
const currentTheme = html.getAttribute('data-theme');
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
html.setAttribute('data-theme', newTheme);
});
</script>
This adds a button that toggles between light and dark themes by changing the data-theme attribute on the HTML element. Your CSS already handles the visual changes through custom properties.
The script uses modern ES modules (type="module") and arrow functions with explicit curly braces. The toggle logic reads the current theme, determines the new theme, and updates the attribute. Your custom properties handle everything else automatically.
What You Will Learn
This assignment reinforces your understanding of CSS custom properties and math functions through practical application. You will gain experience building maintainable design systems, creating fluid responsive layouts without media queries, implementing component variations with scoped properties, and building theme systems that adapt dynamically.
These skills are fundamental to modern CSS development. The patterns you practice here will apply to every project you build, from small landing pages to large applications. Custom properties and math functions are not advanced features you use occasionally but core tools you will use daily.