Skip to content

Visual Depth — box-shadow, text-shadow, and opacity

The STO site now has a color palette, readable typography, and web fonts. This lesson adds the finishing layer: visual depth. Shadows and transparency are among the most satisfying properties to experiment with — small values produce a significant visual impact.

box-shadow adds a shadow to the outside (or inside) of an element. The syntax is:

box-shadow: offset-x offset-y blur-radius spread-radius color;
  • offset-x — horizontal shift. Positive = right, negative = left.
  • offset-y — vertical shift. Positive = down, negative = up.
  • blur-radius — how soft the shadow edges are. 0 = sharp edge, larger = softer.
  • spread-radius — how much larger than the element the shadow is. Positive = expands, negative = contracts. Optional.
  • color — the shadow color. Use rgba() for semi-transparent shadows.
/* Subtle card elevation */
.tour-card {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
}
/* More pronounced elevation */
.tour-card:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
}

The analogy: box-shadow is like the shadow cast by a physical card lifted off a surface. A card held close to a surface casts a small, sharp shadow. A card held higher casts a larger, softer shadow. blur-radius controls the height; offset-y controls the direction.

Separate multiple shadows with commas. The first shadow listed is on top:

.tour-card {
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.08), /* tight ambient shadow */
0 4px 16px rgba(0, 0, 0, 0.12); /* softer elevation shadow */
}

Layering a tight ambient shadow and a softer elevation shadow produces a more realistic result than a single shadow.

The inset keyword moves the shadow inside the element, creating a pressed or recessed effect:

/* Button pressed state */
.btn:active {
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.2);
}
/* Input focus ring alternative */
input:focus {
box-shadow: inset 0 0 0 2px #2c4a1e;
}

An inset shadow with 0 offset and 0 blur but a non-zero spread creates a clean inner border — an alternative to outline that respects border-radius.

Shadows respect border-radius — a rounded card casts a rounded shadow automatically.

.tour-card {
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); /* shadow follows the rounded corners */
}

text-shadow applies a shadow to text characters. The syntax is the same as box-shadow, minus the spread radius:

text-shadow: offset-x offset-y blur-radius color;
/* Subtle text shadow on hero heading for contrast over an image */
.hero h1 {
color: #f5f0eb;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}
/* Multiple text shadows for a glow effect */
.highlight {
text-shadow:
0 0 8px rgba(44, 74, 30, 0.4),
0 0 20px rgba(44, 74, 30, 0.2);
}

text-shadow is most useful on text displayed over a background image, where contrast with the background is uncertain. A small, dark shadow behind light text ensures legibility regardless of the image content.

Use text-shadow sparingly. Overused shadows make text harder to read, not easier.

opacity controls the transparency of an entire element — including all its children and their backgrounds, borders, and text:

.disabled-btn {
opacity: 0.5;
}
.hero-overlay {
opacity: 0.8;
}

Values range from 0 (fully transparent, invisible) to 1 (fully opaque, default).

The key distinction: opacity vs rgba()

  • opacity affects the entire element and everything inside it, including text and child elements.
  • rgba() / hsla() affects only the single property it is applied to.
/* This makes the entire card — text, borders, everything — 50% transparent */
.card {
opacity: 0.5;
}
/* This only makes the background color transparent — text remains fully opaque */
.card {
background-color: rgba(255, 255, 255, 0.5);
}

For overlays, rgba() on the background is almost always the right choice. opacity is useful for disabled states, fade animations, or hover dimming effects.

opacity: 0 vs visibility: hidden vs display: none

Section titled “opacity: 0 vs visibility: hidden vs display: none”
DeclarationVisibleTakes spaceInteractive
opacity: 0NoYesYes (still clickable)
visibility: hiddenNoYesNo
display: noneNoNoNo

opacity: 0 is the correct choice when animating a fade — display and visibility cannot be transitioned.

Add depth to three elements on the STO site:

1. Tour card elevation and hover lift:

.tour-card {
border-radius: 8px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08), 0 4px 12px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.2s ease, transform 0.2s ease;
}
.tour-card:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.14), 0 12px 32px rgba(0, 0, 0, 0.12);
transform: translateY(-2px);
}

2. Hero heading text shadow for image contrast:

.hero h1 {
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
}

3. Button pressed state with inset shadow:

.btn {
background-color: #2c4a1e;
color: #f5f0eb;
padding: 0.75rem 1.5rem;
border: none;
border-radius: 4px;
cursor: pointer;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transition: box-shadow 0.1s ease;
}
.btn:active {
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.25);
transform: translateY(1px);
}

Open the browser and hover over the tour cards. The subtle lift and shadow change makes the cards feel interactive and elevated — a significant improvement from flat rectangles.

  • CSS color formats: named colors (prototyping), hex (fixed brand colors), rgba() (transparency), hsl()/hsla() (palettes and theming).
  • Background properties: background-color as fallback, background-image: url() for images, no-repeat center / cover for hero sections, multiple backgrounds for overlays.
  • Font properties: font-family with a fallback stack, rem units for font-size, font-weight 400/700 for normal/bold, font-style: italic.
  • Text styling: line-height: 1.6 for body readability, letter-spacing for headings and uppercase labels, text-align and text-decoration for links and layout.
  • Web fonts: Google Fonts via <link> method with rel="preconnect" and display=swap; load only needed weights; always include fallbacks.
  • Visual depth: box-shadow with rgba() for card elevation; inset for pressed states; text-shadow for text on images; opacity for whole-element transparency.

Module 05 introduces layout: Flexbox — the modern CSS layout system that makes aligning and distributing elements across rows and columns intuitive and flexible.