1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
---
title: Elevation
summary: >-
  Two shadow levels only. A bounded surface reads by being a step lighter
  than what's behind it, not by a border around its edge; shadow is reserved
  for the one floating layer in a view.
---

## Surface contrast does the work

This system's own rule: no border marks the edge of a bounded surface. A
flat [Card](<%= relative_url('/components/card/') %>) is the default
everywhere — `--gl-color-surface-card` is one neutral step lighter than
`--gl-color-surface-page` around it, and that tonal step is what reads as
"a card," not an outline. Shadow is the exception, not the baseline.

Hairlines have not disappeared from the system — a
[CardRow](<%= relative_url('/components/card-row/') %>) still divides from
its neighbor on one, a table still rules its rows, an underline tab still
sits on one — they just no longer wrap a whole surface. A divider inside a
card; never a border around one.

## The two levels

<ul class="gl-elevation-grid">
  <li>
    <div class="gl-elevation-demo" style="box-shadow: 0 1px 2px rgba(20, 18, 14, 0.05);"></div>
    <div class="gl-specimen__meta"><code>shadow-sm</code></div>
    <p class="gl-muted" style="font-size: var(--gl-step--2); margin: var(--gl-space-3xs) 0 0;">Barely-there separation — a Switch thumb, a focused Input.</p>
  </li>
  <li>
    <div class="gl-elevation-demo" style="box-shadow: 0 2px 6px rgba(20, 18, 14, 0.06), 0 10px 24px -12px rgba(20, 18, 14, 0.18);"></div>
    <div class="gl-specimen__meta"><code>shadow-float</code></div>
    <p class="gl-muted" style="font-size: var(--gl-step--2); margin: var(--gl-space-3xs) 0 0;">The one floating layer in a view. Apply it to at most one element on screen at a time.</p>
  </li>
</ul>

<figure class="example">
<figcaption>Flat vs. float</figcaption>
<div data-layout="grid" data-surface="sunken">
<article class="card">
  <div class="card-body">
    <h3 style="font-size: var(--gl-step--1);">Flat</h3>
    <p>The default. A step lighter than the page, no border, no shadow.</p>
  </div>
</article>
<article class="card" data-elevated>
  <div class="card-body">
    <h3 style="font-size: var(--gl-step--1);">Elevated</h3>
    <p>shadow-float. Reserved for one element per view — a Dialog, a Toast.</p>
  </div>
</article>
</div>
<details><summary>Markup</summary>
<pre><code>&lt;article class="card"&gt;
  &lt;div class="card-body"&gt;
    &lt;h3&gt;Flat&lt;/h3&gt;
    &lt;p&gt;The default. A step lighter than the page, no border, no shadow.&lt;/p&gt;
  &lt;/div&gt;
&lt;/article&gt;
&lt;article class="card" data-elevated&gt;
  &lt;div class="card-body"&gt;
    &lt;h3&gt;Elevated&lt;/h3&gt;
    &lt;p&gt;shadow-float. Reserved for one element per view — a Dialog, a Toast.&lt;/p&gt;
  &lt;/div&gt;
&lt;/article&gt;</code></pre>
</details>
</figure>

## Elevation in the dark theme

Shadows barely register on a near-black surface. The dark theme leans on
surface lightness instead — `--gl-color-surface-card` is one neutral step
lighter than `--gl-color-surface-page`, same as in light mode. The shadow
tokens still apply; they simply stop being the primary signal.

## Rules

- **At most one `--gl-shadow-float` on screen.** If two things both claim to
  be the topmost layer, neither is.
- **No border around a bounded surface, ever.** Surface contrast does the
  identifying work a border would; a card that borders and floats looks like
  a sticker.
- **Never animate a shadow's blur.** If a floated element needs to arrive,
  animate its `opacity`, or do not animate it at all.