CSS Functions
Use CSS's built-in functions — calc(), min(), max(), clamp(), var(), url(), color functions, and the grid-sizing functions repeat()/minmax()/fit-content() — to write layouts and values that compute themselves instead of being hardcoded.
CSS values don't have to be static numbers. CSS has a growing family of functions that let you compute a value at the moment it's used — mixing units, picking the smaller or larger of two values, or clamping a value between a minimum and maximum. These functions are the difference between a stylesheet full of magic numbers and one that adapts itself to its content and viewport.
calc() — do math with mixed units
The syntax has one strict rule that trips up almost every beginner: you MUST have whitespace around + and - operators. calc(100% -40px) is invalid CSS (it parses -40px as a negative value, not a subtraction), while calc(100% - 40px) is correct. Multiplication (*) and division (/) don't need surrounding spaces, but adding them consistently makes expressions easier to read.
• calc(100% - 40px) — full width minus fixed padding, common for sidebars next to a fixed-width nav • calc(100vh - 60px) — full viewport height minus a fixed header • calc(var(--spacing) * 2) — a variable multiplied by a plain number • calc() expressions can nest inside other calc() expressions
min() and max() — pick the smaller or larger value
min() returns the smallest of a comma-separated list of values; max() returns the largest. They're most useful for building 'up to a limit' or 'at least this much' sizing without media queries.
For example, width: min(90%, 600px) means: use 90% of the container's width, but never let it exceed 600px. On a narrow phone screen, 90% is the smaller value and wins. On a wide desktop screen, 600px is the smaller value and wins. That single line replaces what used to require a media query.
clamp() — a fluid value with a floor and a ceiling
clamp() is the function most people reach for once they understand it, especially for fluid typography. It takes three arguments: clamp(MIN, PREFERRED, MAX).
The PREFERRED value is usually something that scales with the viewport, like a vw unit. The browser uses PREFERRED as long as it stays between MIN and MAX; if the fluid value would go below MIN, it uses MIN instead, and if it would exceed MAX, it uses MAX instead.
font-size: clamp(1rem, 2vw + 0.5rem, 2.5rem) means: 'the font size should generally track 2vw + 0.5rem as the viewport changes, but never shrink below 1rem on tiny screens, and never grow past 2.5rem on huge ones.' This gives you one line of CSS that replaces three or four font-size media query breakpoints.
Grid-sizing functions: a quick preview
Three more functions are specific to CSS Grid and are covered fully in the Grid module: repeat() avoids retyping the same track size over and over (repeat(3, 1fr) instead of 1fr 1fr 1fr), minmax() gives a track a size range (minmax(200px, 1fr) means 'never smaller than 200px, but grow to fill available space'), and fit-content() caps a track's size to the smaller of its content size or a given maximum, useful for columns that should hug their content without overflowing.
Function call patterns
/* calc: mixed units, spaces required around + and - */
.sidebar {
width: calc(100% - 40px);
}
/* min/max: pick smaller or larger of a list */
.card {
width: min(90%, 600px);
height: max(200px, 30vh);
}
/* clamp: min, preferred, max */
h1 {
font-size: clamp(1.5rem, 4vw + 1rem, 3rem);
}
/* var with fallback */
.button {
background: var(--brand-color, #3366ff);
}
/* url for images/fonts */
.hero {
background-image: url("images/hero.jpg");
}
/* grid sizing functions */
.grid {
display: grid;
grid-template-columns: repeat(3, minmax(150px, 1fr));
}Code Examples
The heading's font size tracks 5vw as the viewport resizes, but is locked to a floor of 1.75rem on small phones and a ceiling of 3.5rem on large desktop monitors, so it never becomes unreadably small or absurdly large.
font-size: clamp(1.75rem, 5vw, 3.5rem);
Because the header is fixed and removed from normal flow, the hero section needs to know exactly how much vertical space is left. calc(100vh - 70px) computes 'the full viewport height, minus the header's height' so the hero fills the remaining screen exactly, with no gap and no overlap.
This is a popular modern CSS one-liner: the content is as wide as the container minus 32px of breathing room, but never wider than 65 characters (a comfortable reading measure). min() picks whichever constraint is smaller at any given viewport width.
h1 {
font-size: clamp(1.75rem, 5vw, 3.5rem);
line-height: 1.2;
}.header {
height: 70px;
position: fixed;
top: 0;
width: 100%;
}
.hero {
min-height: calc(100vh - 70px);
margin-top: 70px;
}.article-content {
width: min(100% - 32px, 65ch);
margin-inline: auto;
}clamp(MIN, PREFERRED, MAX)
Below a certain viewport width the value is held at MIN; in the middle range it scales fluidly with PREFERRED; above a certain width it is held at MAX.
value
^
MAX|-------------------_________
| /
| / <- tracks PREFERRED (e.g. 5vw)
| /
MIN|__________/
+------------------------------> viewport width
clamped to MIN fluid zone clamped to MAXImportant Rules
Always put spaces around + and - inside calc() — calc(100% -10px) is parsed incorrectly, calc(100% - 10px) is correct.
• and / inside calc() do not require surrounding spaces, but adding them is good style.
clamp() arguments go in the order min, preferred, max — swapping the order silently breaks the intended behavior.
min(), max(), and clamp() can take any number of comma-separated arguments, not just two or three.
These functions can be nested inside each other, e.g. width: calc(min(90vw, 800px) - 2rem).
Where these functions matter on EngiLipi
EngiLipi's lesson pages use clamp() for every heading so text scales smoothly from a phone to an ultrawide monitor without a pile of font-size media queries. The code-editor panels use calc(100vh - var(--header-height)) so the practice compiler fills exactly the remaining screen height under the fixed top navigation, and the article reading column uses min(100% - 32px, 70ch) to keep code walkthroughs comfortably readable at any width.
width: calc(100%-40px);
width: calc(100% - 40px);
Without spaces around the minus sign, the parser reads -40px as a single negative value token rather than a subtraction operator, and the whole calc() expression becomes invalid — the browser silently falls back to the property's default.
font-size: clamp(3rem, 1rem, 1.5vw);
font-size: clamp(1rem, 1.5vw, 3rem);
The arguments must be ordered min, preferred, max. Reversed like this, the 'minimum' (3rem) is larger than the 'maximum' (1rem), producing confusing, unintended sizing.
[Predict the Output] Given font-size: clamp(1rem, 8vw, 4rem);, on a very narrow phone screen where 8vw evaluates to roughly 0.4rem, what font-size does the browser actually render? A. 0.4rem, because clamp() always uses the preferred value B. 1rem, because the preferred value fell below the minimum, so the minimum is used instead C. 4rem, because clamp() defaults to the maximum on small screens D. The declaration is invalid on narrow screens and font-size falls back to the browser default
B. 1rem, because the preferred value fell below the minimum, so the minimum is used instead — clamp(MIN, PREFERRED, MAX) uses PREFERRED only while it falls between MIN and MAX. Here 8vw computes to about 0.4rem, which is below the 1rem minimum, so the browser clamps up to 1rem — the floor value, not the preferred one.
Write a CSS rule for a.card element whose width is min(90%, 500px), whose padding uses clamp() to scale between 1rem and 2rem based on 3vw, and whose height is calc(100% - 20px) relative to its parent..card { width: min(90%, 500px); padding: clamp(1rem, 3vw, 2rem); height: calc(100% - 20px); }
Remember clamp() takes exactly three arguments in min, preferred, max order, and calc() needs a space on both sides of the minus sign.