Portal | Level: L0: Entry | Topics: CSS | Domain: DevOps & Tooling
CSS Fundamentals - Primer¶
Why This Matters¶
As a DevOps or SRE engineer, you will encounter CSS more often than you expect. Internal dashboards, status pages, documentation sites, Grafana panels with custom HTML, and CI/CD pipeline UIs all involve CSS. You do not need to be a frontend developer, but you need enough fluency to fix a broken layout, adjust a status page during an incident, or maintain an internal tool without calling in a specialist.
CSS is deceptively simple to start with — you change a color, move a margin, and it works. The difficulty emerges when layout modes, specificity rules, and browser defaults interact in ways you did not anticipate. Understanding the box model, display modes, and the cascade eliminates most of that confusion.
The browser DevTools inspector is your primary debugging tool. If you are guessing at why a layout looks wrong instead of inspecting computed styles, you are wasting time.
Core Concepts¶
1. The Box Model¶
Every HTML element is a rectangular box with four layers:
+--------------------------------------------+
| margin |
| +--------------------------------------+ |
| | border | |
| | +--------------------------------+ | |
| | | padding | | |
| | | +--------------------------+ | | |
| | | | content | | | |
| | | +--------------------------+ | | |
| | +--------------------------------+ | |
| +--------------------------------------+ |
+--------------------------------------------+
By default (box-sizing: content-box), width and height apply only to the
content area. Padding and border are added on top, making the element larger
than the width you specified. This is almost always surprising.
/* Fix this globally — nearly every modern project does this */
*, *::before, *::after {
box-sizing: border-box;
}
With border-box, width includes content + padding + border. Margin is
always outside.
Name origin: CSS stands for "Cascading Style Sheets." The "cascade" refers to the algorithm that determines which style rule wins when multiple rules apply to the same element. CSS was proposed by Hakon Wium Lie in 1994 while working at CERN with Tim Berners-Lee. The first browser to fully support CSS1 was Internet Explorer 5 for Mac (2000).
2. Display Modes¶
The display property controls how an element participates in layout.
| Value | Behavior |
|---|---|
inline |
Flows with text. Width/height ignored. |
block |
Takes full width. Stacks vertically. |
inline-block |
Flows inline but respects width/height. |
flex |
Children laid out in a flex container. |
grid |
Children laid out in a grid container. |
none |
Removed from layout entirely. |
/* Flexbox — single-axis layout (row or column) */
.navbar {
display: flex;
justify-content: space-between; /* main axis */
align-items: center; /* cross axis */
gap: 1rem;
}
/* Grid — two-axis layout */
.dashboard {
display: grid;
grid-template-columns: 250px 1fr 1fr;
grid-template-rows: auto 1fr auto;
gap: 1rem;
}
Rule of thumb: use flexbox for one-dimensional layouts (navbars, card rows, centered content). Use grid for two-dimensional layouts (dashboards, page scaffolding).
Remember: Mnemonic: "Flex = one direction, Grid = two directions." If you're laying things out along a single axis (a row of buttons, a column of cards), use flexbox. If you need rows AND columns simultaneously (a dashboard layout), use grid.
3. Selectors and Specificity¶
CSS rules are applied based on selector specificity. When multiple rules target the same element, the more specific rule wins.
Specificity is calculated as (a, b, c):
a = inline styles (style="...")
b = ID selectors (#header)
c = classes, attributes, pseudo-classes (.active, [type="text"], :hover)
(element selectors and pseudo-elements have lowest priority)
Examples:
p → (0, 0, 1)
.warning → (0, 1, 0)
#header → (1, 0, 0)
#header .nav a → (1, 1, 1)
style="color: red" → wins over all non-!important rules
/* Low specificity — easy to override */
p { color: black; }
/* Higher specificity — overrides the above for .error paragraphs */
p.error { color: red; }
/* !important — nuclear option, avoid in production CSS */
p { color: blue !important; }
The cascade resolution order:
1. Origin and importance (!important > normal)
2. Specificity (higher wins)
3. Source order (later rule wins if specificity is equal)
4. Common Layout Patterns¶
/* Center something horizontally and vertically */
.centered {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
/* Sticky footer (page content pushes footer down) */
body {
display: flex;
flex-direction: column;
min-height: 100vh;
}
main { flex: 1; }
footer { /* stays at bottom */ }
/* Responsive sidebar layout */
.layout {
display: grid;
grid-template-columns: minmax(200px, 300px) 1fr;
}
@media (max-width: 768px) {
.layout {
grid-template-columns: 1fr;
}
}
5. Media Queries¶
Media queries let you apply CSS rules conditionally based on viewport size, device capabilities, or user preferences. The most common use is responsive layout — making a page work on both desktop and mobile.
Mobile-first approach: write default styles for the smallest screen, then add complexity for wider viewports. This is simpler than the reverse because mobile styles are usually the simplest case.
/* Default: single-column, sidebar hidden */
.sidebar { display: none; }
.content { padding: 1rem; }
/* 768px+: show sidebar, two-column layout */
@media (min-width: 768px) {
.layout {
display: grid;
grid-template-columns: 250px 1fr;
gap: 1rem;
}
.sidebar { display: block; }
}
/* 1200px+: wider sidebar, more padding */
@media (min-width: 1200px) {
.layout {
grid-template-columns: 300px 1fr;
padding: 2rem;
}
}
Why mobile-first works better: if you start with desktop styles and override for mobile, you end up with heavy CSS that mobile devices download but mostly discard. Starting small and adding is simpler to maintain and faster to load.
Common breakpoints: 768px (tablet), 1024px (small desktop), 1200px (large desktop). These are conventions, not rules — test your actual layout and break where it breaks.
Other useful media queries:
/* Print: strip navigation, backgrounds, ads — leave content */
@media print {
nav, .sidebar, .no-print { display: none; }
body { font-size: 12pt; color: black; background: white; }
a { text-decoration: underline; color: black; }
/* Show URLs after links so printed page is useful */
a[href]::after { content: " (" attr(href) ")"; }
}
/* Dark mode: respect OS/browser preference */
@media (prefers-color-scheme: dark) {
:root {
--bg: #1a1a1a;
--text: #e0e0e0;
--border: #333;
}
body { background: var(--bg); color: var(--text); }
}
/* Reduced motion: disable animations for accessibility */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
6. CSS Variables (Custom Properties)¶
CSS variables (custom properties) let you define values once and reference them everywhere. They cascade like any other CSS property and can be overridden in child scopes.
:root {
--color-ok: #22c55e;
--color-warn: #eab308;
--color-critical: #ef4444;
--spacing: 1rem;
--radius: 4px;
}
.status-ok { color: var(--color-ok); }
.status-warn { color: var(--color-warn); }
.status-critical { color: var(--color-critical); }
Why they matter for internal tools: when you need to change a brand color or adjust spacing across 20 components, you change one variable instead of hunting through 50 rules. They also enable theming:
/* Override variables for a dark section */
.dark-panel {
--bg: #1a1a1a;
--text: #e0e0e0;
}
/* All children of .dark-panel inherit the overrides */
.card {
background: var(--bg, white); /* fallback: white */
color: var(--text, black); /* fallback: black */
padding: var(--spacing);
border-radius: var(--radius);
}
The var(--name, fallback) syntax provides a default when the
variable is not set — defensive coding for CSS.
Gotcha: CSS custom properties (variables) are resolved at runtime, not at build time like Sass/LESS variables. This means they can be changed dynamically via JavaScript (
element.style.setProperty('--bg', '#333')) and they cascade through the DOM. This is powerful for theming but means typos in variable names silently fall back to the default or produce invisible failures.
7. Position and Stacking¶
The position property controls how an element is placed relative to
its normal position, its parent, or the viewport.
| Value | Behavior | Use Case |
|---|---|---|
static |
Default flow. top/left ignored. |
— |
relative |
Offset from normal pos. Space preserved. | Nudging, positioning anchor |
absolute |
Removed from flow. Vs nearest positioned ancestor. | Tooltips, dropdowns, badges |
fixed |
Removed from flow. Positioned vs viewport. | Persistent headers, floating buttons |
sticky |
Normal flow until scroll threshold, then fixed. | Table headers, section headers |
The most common confusion: absolute positions relative to the
nearest ancestor that has position set to anything except static.
If no ancestor is positioned, it positions relative to the document
body — which is almost never what you want.
/* Parent must be positioned for child absolute to work */
.dropdown-wrapper {
position: relative; /* creates the positioning context */
}
.dropdown-menu {
position: absolute; /* relative to .dropdown-wrapper */
top: 100%; /* just below the wrapper */
left: 0;
z-index: 10;
}
/* Sticky header — stays at top when scrolled past */
.page-header {
position: sticky;
top: 0;
z-index: 100;
background: white; /* opaque or content shows through */
}
z-index only works on positioned elements (anything except
static). But z-index is not global — each element with opacity,
transform, filter, or will-change creates a new stacking
context. An element with z-index: 9999 inside a stacking context
with z-index: 1 will still appear behind an element with
z-index: 2 in a sibling context.
8. Debugging CSS¶
The browser DevTools inspector is the single most important CSS debugging tool. If you are changing CSS in a file and refreshing to see what happened, you are working too slowly.
Core debugging workflow:
- Right-click element → Inspect
- Check the Computed tab — shows final resolved values
- Check the Styles panel — shows which rules apply and which are overridden (struck-through)
- Toggle rules on/off with checkboxes to isolate the cause
- Edit values live in the panel to test fixes before changing code
Common debugging patterns:
/* Make element boundaries visible (outline, not border —
outlines do not affect layout or trigger reflows) */
* { outline: 1px solid red; }
/* Or target specific sections */
.layout > * { outline: 1px solid blue; }
Common layout problems and causes:
| Symptom | Likely Cause |
|---|---|
| Element wider than viewport | Missing box-sizing: border-box or fixed width |
| Unexpected gap between elements | Margin collapse (vertical margins merge, larger wins) |
z-index not working |
Element is position: static or stacking context issue |
| Flex children not wrapping | Missing flex-wrap: wrap on container |
| Grid item in wrong cell | Implicit grid created extra tracks — check grid-template-* |
absolute element in wrong place |
Nearest positioned ancestor is not what you expected |
| Text overflowing container | Missing overflow-wrap: break-word or min-width: 0 on flex child |
What Experienced People Know¶
- Always set
box-sizing: border-boxglobally. The content-box default has caused more layout bugs than any other single CSS behavior. - The browser inspector's computed styles panel tells you exactly what values are active. Use it instead of guessing.
- Margin collapse is real: adjacent vertical margins do not add — the larger one wins. This is by design but catches people off guard.
- Flexbox
gapis now well-supported everywhere. Stop using margin hacks for spacing between flex children. z-indexonly works on positioned elements (anything except static). Stacking contexts are created by several properties including opacity, transform, and filter — not just z-index.- When debugging layout, add
outline: 1px solid red(not border — outlines do not affect layout) to see element boundaries. - CSS specificity wars are a sign of structural problems. If you are
reaching for
!important, reconsider your selector strategy. - For internal tools, a CSS reset or normalize.css eliminates browser default inconsistencies. This saves significant debugging time.
remunits (relative to root font size) are more predictable thanem(relative to parent) for spacing and sizing.
Debug clue: If a layout looks correct in Chrome but broken in Firefox or Safari, the most common culprit is implicit
min-width: autoon flex items. Flex items default to not shrinking below their content size, and browsers interpret this slightly differently. Addingmin-width: 0oroverflow: hiddento the flex child usually fixes cross-browser flex layout issues.
Wiki Navigation¶
Related Content¶
- CSS Flashcards (CLI) (flashcard_deck, L1) — CSS