Showing the changes to apps/web/src/components/pullRequest (1 of 4 files). Show all
Unified · Split
| @@ -3,11 +3,9 @@ · 2 unmodified lines | |||
| 3 | * and a detail panel opening — use bars in the geometry of the content they stand for, pulsing | 3 | * and a detail panel opening — use bars in the geometry of the content they stand for, pulsing |
| 4 | * on one composited layer. Diff loading uses the shared diff-panel skeleton instead. | 4 | * on one composited layer. Diff loading uses the shared diff-panel skeleton instead. |
| 5 | * | 5 | * |
| 6 | * Deliberately not the app's shimmer skeleton. The sweep is a `transform` animation per bar — | 6 | * The bars share the app-wide `Skeleton` tone (`muted-foreground` at low alpha, which reads on |
| 7 | * compositor-safe, but a layer for every bar on screen — and its white highlight over the | 7 | * both themes) and the single `animate-skeleton` pulse, applied once on the container so any |
| 8 | * near-white `muted` base all but disappears in light mode. Here one `animate-ghost-pulse` on the | 8 | * number of bars costs one opacity animation. |
| 9 | * container is a single opacity animation however many bars sit under it, and the bars take | ||
| 10 | * their tone from `muted-foreground` at low alpha, which reads on both themes. | ||
| 11 | */ | 9 | */ |
| 12 | import { cn } from "~/lib/utils"; | 10 | import { cn } from "~/lib/utils"; |
| 13 | 11 | ||
| @@ -32,7 +30,7 @@ export function PullRequestListGhost({ · 18 unmodified lines | |||
| 32 | <div | 30 | <div |
| 33 | role="status" | 31 | role="status" |
| 34 | aria-label={caption ?? "Loading pull requests"} | 32 | aria-label={caption ?? "Loading pull requests"} |
| 35 | className="animate-ghost-pulse space-y-0.5" | 33 | className="motion-safe:animate-skeleton space-y-0.5" |
| 36 | > | 34 | > |
| 37 | {caption ? ( | 35 | {caption ? ( |
| 38 | <p className="px-3 pb-1 text-xs font-medium text-muted-foreground/70">{caption}</p> | 36 | <p className="px-3 pb-1 text-xs font-medium text-muted-foreground/70">{caption}</p> |
| @@ -67,7 +65,7 @@ export function PullRequestDetailGhost() { · 28 unmodified lines | |||
| 67 | <div | 65 | <div |
| 68 | role="status" | 66 | role="status" |
| 69 | aria-label="Loading pull request" | 67 | aria-label="Loading pull request" |
| 70 | className="animate-ghost-pulse flex h-full min-h-0 flex-col overflow-hidden bg-background" | 68 | className="motion-safe:animate-skeleton flex h-full min-h-0 flex-col overflow-hidden bg-background" |
| 71 | > | 69 | > |
| 72 | <div className="shrink-0 border-b border-border/60"> | 70 | <div className="shrink-0 border-b border-border/60"> |
| 73 | <div className="flex h-7 items-center justify-between gap-3 px-4"> | 71 | <div className="flex h-7 items-center justify-between gap-3 px-4"> |
| @@ -160,7 +158,11 @@ export function PullRequestDetailGhost() { · 86 unmodified lines | |||
| 160 | /** People-shaped: an avatar and a name, in the reviewer picker's own row height. */ | 158 | /** People-shaped: an avatar and a name, in the reviewer picker's own row height. */ |
| 161 | export function PullRequestPeopleGhost({ rows = 4 }: { rows?: number }) { | 159 | export function PullRequestPeopleGhost({ rows = 4 }: { rows?: number }) { |
| 162 | return ( | 160 | return ( |
| 163 | <div role="status" aria-label="Loading people" className="animate-ghost-pulse space-y-1 p-1"> | 161 | <div |
| 162 | role="status" | ||
| 163 | aria-label="Loading people" | ||
| 164 | className="motion-safe:animate-skeleton space-y-1 p-1" | ||
| 165 | > | ||
| 164 | {Array.from({ length: rows }, (_, index) => ( | 166 | {Array.from({ length: rows }, (_, index) => ( |
| 165 | <div key={index} className="flex h-7 items-center gap-2 rounded-md px-2"> | 167 | <div key={index} className="flex h-7 items-center gap-2 rounded-md px-2"> |
| 166 | <GhostBar className="size-4 rounded-full" /> | 168 | <GhostBar className="size-4 rounded-full" /> |
| @@ -174,7 +176,11 @@ export function PullRequestPeopleGhost({ rows = 4 }: { rows?: number }) { · 7 unmodified lines | |||
| 174 | /** The timeline's own shape: dots on the rail, a line and a date to each. */ | 176 | /** The timeline's own shape: dots on the rail, a line and a date to each. */ |
| 175 | export function PullRequestTimelineGhost({ rows = 6 }: { rows?: number }) { | 177 | export function PullRequestTimelineGhost({ rows = 6 }: { rows?: number }) { |
| 176 | return ( | 178 | return ( |
| 177 | <div role="status" aria-label="Loading timeline" className="animate-ghost-pulse px-4 py-5"> | 179 | <div |
| 180 | role="status" | ||
| 181 | aria-label="Loading timeline" | ||
| 182 | className="motion-safe:animate-skeleton px-4 py-5" | ||
| 183 | > | ||
| 178 | <div className="relative ml-2 border-l border-border/70 pl-5"> | 184 | <div className="relative ml-2 border-l border-border/70 pl-5"> |
| 179 | {Array.from({ length: rows }, (_, index) => ( | 185 | {Array.from({ length: rows }, (_, index) => ( |
| 180 | <div key={index} className="relative pb-5"> | 186 | <div key={index} className="relative pb-5"> |
| @@ -194,7 +200,7 @@ export function PullRequestConversationGhost({ rows = 3 }: { rows?: number }) { · 13 unmodified lines | |||
| 194 | <div | 200 | <div |
| 195 | role="status" | 201 | role="status" |
| 196 | aria-label="Loading pull request conversation" | 202 | aria-label="Loading pull request conversation" |
| 197 | className="animate-ghost-pulse space-y-4 py-2" | 203 | className="motion-safe:animate-skeleton space-y-4 py-2" |
| 198 | > | 204 | > |
| 199 | {Array.from({ length: rows }, (_, index) => ( | 205 | {Array.from({ length: rows }, (_, index) => ( |
| 200 | <div key={index} className="flex items-start gap-2"> | 206 | <div key={index} className="flex items-start gap-2"> |