Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,18 @@
opacity: 0; transition: opacity .35s ease;
}
.c-photo.on { opacity: 1; }
/* The media slot never disappears when OSU has no photograph for a room.
Initials give the empty frame a building-specific identity, and stay behind
the canvas so the same layer is revealed if an image fails to load. */
.c-fallback {
position: absolute; inset: 0;
display: grid; place-items: center;
background:
radial-gradient(circle at 30% 25%, rgba(255,255,255,.08), transparent 45%),
var(--card-hi);
color: var(--dim); font-size: clamp(4rem, 10vw, 8rem); font-weight: 750;
letter-spacing: -.06em;
}
/* An img is natively draggable, and a pointerdown on one starts a browser
image-drag that CANCELS the pointer stream. The swipe then saw a
pointercancel instead of a move and put the card straight back, so a card
Expand Down Expand Up @@ -1055,13 +1067,6 @@
body.carding .c-act .ico { width: 1.35rem; height: 1.35rem; filter: none; }
body.carding .c-act-label { display: inline; }
body.carding .c-stamp { display: none; }
body.carding .c-card.plain {
grid-template-columns: minmax(20rem, 34rem); grid-template-areas: "details";
justify-content: center;
}
body.carding .c-card.plain .c-media { display: none; }
body.carding .c-card.plain .c-plate,
body.carding .c-card.plain .c-acts { grid-area: details; }
}

/* The cold card sits above everything, including the note pill, because when
Expand Down
7 changes: 7 additions & 0 deletions js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -1462,6 +1462,11 @@ function cardParts(r) {
return { building: n ? name : '', room: n || name || r.id };
}

function buildingInitials(name) {
const words = String(shortName(name) || '').match(/[A-Za-z0-9]+/g) ?? [];
return words.slice(0, 3).map((word) => word[0]).join('').toUpperCase() || '?';
}

// One room. The count and walk cap stay on the list. A missing event snapshot
// does need a short warning here because the card is a standalone answer.
//
Expand Down Expand Up @@ -1510,6 +1515,7 @@ function paintCard() {
}

const { building, room } = cardParts(r);
const initials = buildingInitials(r.name);
const photo = photoFor(r.id);
const win = windowOf(r);
const short = shortWindow(win);
Expand Down Expand Up @@ -1560,6 +1566,7 @@ function paintCard() {
<article class="c-card${photo ? '' : ' plain'}" id="c-top" tabindex="0"
role="group" aria-label="${esc(said)}">
<div class="c-media">
<span class="c-fallback" aria-hidden="true">${esc(initials)}</span>
${photo ? `<canvas class="c-photo" id="c-img" aria-hidden="true"></canvas>` : ''}
<span class="c-scrim" aria-hidden="true"></span>
</div>
Expand Down
3 changes: 3 additions & 0 deletions scripts/test/screens.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1415,7 +1415,10 @@ test('a room with no photograph gets a card, not a broken frame', () => {
assert.match(src, /state\.photos\?\.has\(id\)/);
assert.match(src, /: null/);
const paint = bodyOf('paintCard');
const html = readFileSync(join(ROOT, 'index.html'), 'utf8');
assert.match(paint, /photo \? '' : ' plain'/, 'a photoless room stopped getting the plain card');
assert.match(paint, /c-fallback/, 'a photoless room lost its building initials');
assert.doesNotMatch(html, /\.c-card\.plain \.c-media\s*\{\s*display:\s*none/, 'desktop hides the media slot again');
// And a file that 404s or decodes to nothing falls back to the same card
// rather than leaving an empty frame under the plate.
assert.match(paint, /source\.onerror/);
Expand Down
Loading