Renovating Bingo
From a legacy Bootstrap app to a TNT SPARK Edition —
what changed, what didn’t, and why every decision matters.
This log documents the session that renovated a working JavaScript Bingo game from a legacy Bootstrap 5.0.2 structure into the 2026 TNT SPARK ecosystem. The focus is on how decisions were made — which parts to preserve exactly, which to change, and why a game app warrants different visual choices than a classroom tool or a simulations demo.
Watch for Prompt Critique boxes (amber) and Design Decision boxes (teal). The code is a side effect of these decisions.
I have a bingo app that works as desired with a pleasing design ‘look’ but I’d like to upgrade it to fit into our TNT ecosystem. You did similar work for me with the classroomDial app, attached. As we did with the classroomDial page, where you curated a ‘chatlog’ to show our development work as a training tool, I’d like to do the same for the bingo app. As we upgrade it, I’d like to showcase and teach about the changes we make in a document: bingoChatlog.html. Can you make the preliminary changes to bingoIndex.html (I’m guessing this means changing out the navbar and footer) and possibly renovating the hero page. From there, we will decide on further upgrades.
This prompt does several things well:
- Establishes what is not broken — “works as desired with a pleasing design’look’” signals immediately that functionality is sacred and the goal is integration, not replacement. This rules out any temptation to rewrite the JavaScript or restyle the bingo card itself.
- References a concrete precedent — providing the classroomDial files as a reference gives the AI a living example of what a finished TNT SPARK renovation looks like. Words like “ecosystem” and “SPARK chatlog” become precise because the reference makes them concrete.
- Scopes to preliminary changes — naming the three specific areas (navbar, footer, hero) keeps the first pass focused. This is good engineering discipline: renovate in layers, validate each layer before expanding scope.
- Commits to the chatlog as a deliverable — making the chatlog part of the task, not an afterthought, means the documentation gets the same care as the code.
The one area for improvement: the prompt doesn’t specify the tone of the hero.
“Pleasing design look” is subjective. Knowing whether to go light (classroom tool feel)
or dark (game/casino feel) is a design decision that needed to be resolved before writing the CSS.
The AI read the existing bingo-balls.jpg background and the “festive” nature
of the app and made a judgment call — but it is always better to state the intent explicitly.
After reading bingoIndex.html, bingo-styles.css, and the classroomDial reference
files, here is the analysis before touching anything:
What to preserve entirely:
- All JavaScript logic — the card generator, draw mechanics, token toggle, localStorage persistence, offcanvas settings panel, and alert modal. This code is mature and tested. Zero changes to behavior.
- The bingo card HTML structure — the 5×5 table with its
bs/is/ns/gs/osclass system. The JS depends on these class names for every draw and token operation. - The offcanvas sidebar and alert modal — both Bootstrap 5 components that upgrade cleanly to 5.3.3 without any HTML changes.
What to change:
- Bootstrap: 5.0.2 → 5.3.3 (head CSS and body JS links)
- Google Fonts: Alfa Slab One + Roboto Condensed → Boogaloo + Inter (TNT brand)
- Add Font Awesome 6.5
- Add
tnt-base-styles.cssand createbingoSPARKStyles.css - Add SEO meta block
- Replace the white light navbar with the TNT dark
#mainNavpattern - Replace the jumbotron + cage div with a proper
<section id="hero">; relocate the cage div to the app section - Move the footer outside
<main>and replace with the TNT dark footer pattern - Add
linkGuard.js - Fix one existing accessibility bug:
<label for="check1">referenced a non-existent id; corrected tofor="operatorChkBox"to match the actual input
The most important pre-renovation finding was the relationship between the JavaScript and the HTML.
Specifically: bingoCageDiv lived inside the jumbotron because the original developer put the operator
controls next to the heading. The JS doesn’t care where bingoCageDiv is in the DOM —
it only cares that document.getElementById("bingoCageDiv") returns an element. This meant the cage
panel could be freely moved to the app section without touching a single line of JavaScript.
This is the core benefit of the read-first discipline: knowing what the code assumes tells you what HTML constraints actually exist, versus what constraints are just coincidental structure. The jumbotron location was coincidental. The element ID was a real constraint. Only one of those needed to be preserved.
The key question for the hero design was: dark overlay or light overlay over
bingo-balls.jpg? The classroomDial chatlog documented the light overlay choice
for the spinner. Bingo presents a different context:
- It is not a projection tool. Players are on their own devices. Projector brightness is not a constraint. The dark-room energy of a casino or game night is the right reference point.
- The existing festive palette leans dark and saturated.
The
Festive-Background.webptile is dark. The bingo card has a red header (#E22200). The token colors are vivid primaries. A light hero overlay would fight those choices; a dark overlay extends them. - Game apps in the TNT ecosystem are dark. The Slot Machine uses a dark casino palette. Magic 8 Ball uses a dark sphere on dark background. Bingo belongs in that family.
The hero overlay gradient chosen:
rgba(0, 10, 60, 0.58) → rgba(180, 0, 30, 0.48) — deep navy-to-red,
which matches the bingo card’s red header and creates a cinematic game-night feel
while keeping the colorful bingo balls subtly visible underneath.
The overlay gradient is not just a dimmer. It is a design signal. The two-color gradient — navy on the left, dark red on the right — does three things:
- The navy picks up the bingo ball blue tones visible in
bingo-balls.jpg - The dark red previews the red card header the player will see immediately below
- The diagonal direction (135°) creates visual movement, appropriate for a game that is literally about drawing and randomness
Compare this to the classroomDial hero (rgba(255,255,255,0.60) →
rgba(200,255,210,0.55)): a blue-to-green pastel that previews the blue controls and
green canvas of the spinner below. In both cases, the gradient is chosen to bridge the hero visually
into the content below it. The hero should never feel like a separate page element stuck on top —
it should flow into the app.
Here is the complete change inventory for bingoIndex.html, with a reason for each change:
Head section:
- Bootstrap 5.0.2 → 5.3.3 (both CSS and JS links). No HTML changes required; all Bootstrap 5 data attributes are backward-compatible across minor and patch versions.
- Removed
id="bsStyleLink"andid="devStyleLink"from the link elements. These IDs were not referenced anywhere in the JavaScript; removing them is harmless and avoids misleading future developers. - Replaced Alfa Slab One + Roboto Condensed with Boogaloo + Inter — the TNT brand font pairing. Alfa Slab One is display-heavy but narrow; Boogaloo has the same playful energy at a wider tracking that reads better as a hero title on varied screen widths.
- Added Font Awesome 6.5 — enables the icon language used throughout the SPARK ecosystem (arrows, checks, labels in the chatlog and footer).
- Replaced
bingo-styles.csswithtnt-base-styles.css+bingoSPARKStyles.css. The newbingoSPARKStyles.csscarries forward the color and layout rules that still apply; the legacybingo-styles.cssis preserved on disk for reference and can be retired in a future pass. - Added the full SEO meta block (description, keywords, author, og:type, og:title, og:url, og:image).
- Updated
lastUpdatefrom"07/11/2026"to"07/13/2026".
Navbar:
- Replaced the white
navbar-light bg-lightbar with the TNT dark#mainNavpattern. The old navbar had a single link:../gamesIndex.html. That file does not exist in the workspace. The new navbar uses the standard TNT site links, with JS Apps markedactive— Bingo is a DOM-based JavaScript game, not a p5.js canvas app, so JS Apps is the correct classification. - The bingo cage icon that served as the navbar brand was removed. The TNT navbar uses the shared dynamite icon + wordmark. The bingo icon now appears in the hero heading where it has more visual impact.
Hero:
- Replaced
div#jumbotronwith a proper<section id="hero">. This is a semantic improvement: the old div was styled as a hero but marked up as an anonymous container. A<section>with anaria-labeltells screen readers and search engines what the region is. - Moved
bingoCageDivout of the hero and into the app section. The cage panel is a functional control, not a branding element. It should live with the other controls, not embedded in the site’s visual header. The JavaScript does not care where in the DOM it lives. - Added the bingo cage icon (
noun-bingo-1848078-0177AA.png) to the hero<h1>with a white CSS filter — the same technique used for the fortune wheel icon in the classroomDial hero. This makes the bingo icon white-on-dark, matching the hero text and maintaining readability over the dark overlay.
Body class:
- Removed
class="pb-0 bg-dark"from<body>. Thebg-darkwas needed when the body was the fallback background (before the footer became dark and main had the festive tile). Now the festive background is scoped to#appSectioninbingoSPARKStyles.css, so the body needs no override class.
Footer:
- Moved footer outside
<main>. The original used Bootstrap’s flexbox sticky-footer pattern (footer inside amin-vh-100flex main). The TNT pattern puts the footer after main — simpler and more consistent. - Replaced the light Bootstrap footer with the TNT dark footer, using
.footer-iconsand.footer-textclasses fromtnt-base-styles.css. - Updated icon paths. The original footer pointed to images in the local
images/folder. The TNT footer icons live at../images/(site level), consistent with every other TNT SPARK page. - Added the Validate this page icon (
validateLink) with localhost detection, consistent with all other SPARK pages. - Added the Copilot + VS Code credits in the footer paragraph — TNT SPARK pages acknowledge their AI-assisted development.
JavaScript:
- All game logic is unchanged. The only edits to the
<script>block are insideinit(): replaced two separate variable declarations forupdateSpanandcopyrightSpanwith directgetElementByIdcalls (cleaner, same behavior), updated the copyright format to use an en-dash, and added thevalidateLinklocalhost-detection block. - Added
linkGuard.jsat the bottom of the body.
Accessibility fix:
- The offcanvas checkbox label had
for="check1"but the input ID is"operatorChkBox". A mismatchedforattribute means clicking the label text does not toggle the checkbox — it silently does nothing. Fixed tofor="operatorChkBox". This is the kind of bug that is invisible in testing but real for keyboard and screen-reader users.
Three CSS files are involved in the Bingo SPARK edition:
../styles/tnt-base-styles.css— shared across all TNT pages. Defines CSS custom properties (--tnt-red,--tnt-dark,--boogaloo, etc.), the#mainNavnavbar, the#herostructural rules, and the dark footer. This file is never edited for app-specific changes.styles/bingoSPARKStyles.css— new, specific to this app. Defines the hero background image and gradient overlay, the app section’s festive tile background, the cage operator panel layout, and the inherited color tokens (.bg-custom-color1for the card header). This is the file to edit when the Bingo app needs visual changes.styles/bingo-styles.css— the legacy file, preserved on disk. Contains the old jumbotron and navbar styles (now dead selectors that match no elements), the old footer rules (overridden by the base styles), and the media queries (partially superseded). It is no longer linked frombingoIndex.html. Keeping it on disk means it can be referenced during future refinement passes before being retired.
The inline <style> block in bingoIndex.html is
intentionally retained for this preliminary pass. It holds the bingo-specific game
rules: token colors, table cell sizing, and the free-space nudge. Moving these to
bingoSPARKStyles.css is a clean future task — but it is not a
preliminary change, and doing it now would be scope creep.
The user said “preliminary changes.” That phrase has a specific meaning:
do the structural work that enables further improvements, without over-engineering the first pass.
The inline <style> block in bingoIndex.html contains working,
correct CSS that governs the game mechanics (token colors, cell sizing). Moving it to an external
file would produce identical visual output. It is a cleanup task, not a functional improvement.
The rule for when to leave inline styles during a preliminary pass:
- If the styles are correct (they produce the intended behavior)
- And the styles are game-specific (not conflicting with shared styles)
- And moving them is not required to make the structural changes work
Then leave them. Document the debt. Move them in a subsequent pass when the scope is explicitly “clean up CSS.” Doing it unprompted during a structural renovation is a recipe for introducing bugs in code that was working fine.
In the original code, bingoCageDiv lived inside
div#jumbotron alongside the <h1> heading. This created
an odd layout: when the cage operator was active, a panel with a clickable bingo cage
image floated at the top-right of the hero area. When it was hidden, nothing was there.
The renovation moves the cage panel to the top of the app section — above the bingo card, centered. This is better for three reasons:
- The hero is a branding region. It should communicate what the app is, not expose operational controls. The cage panel is a control.
- The cage panel and the bingo card are functionally related — you draw from the cage and mark the card. Having them in the same section reduces the visual distance between cause and effect.
- The cage panel starts hidden (
d-none). An empty hero looks fine; an empty hero with a ghost layout reserved for the cage panel looks broken.
The new CSS for #bingoCageDiv in bingoSPARKStyles.css
gives it a card-like appearance: white background with slight transparency, rounded
corners, amber border, and a drop shadow. It looks like a control panel that belongs
above a game board — which is exactly what it is.
A hero section’s job is to answer one question in three seconds: What is this page? It does this through image, headline, and subtitle. Adding an interactive control (the cage drawing interface) into the hero creates a second job for the region and dilutes its identity function.
There are exceptions — some apps legitimately put a primary control in the hero (a search bar on a search app, a “Start Game” button on a title screen). The cage panel is not that: it is a secondary feature that only activates for users who have specifically opted into the cage operator role in the settings. It belongs in the content area, not the branding area.
The broader principle: when you are renovating a hero, move everything out of it that is not identity-communicating. If something could be on a dedicated controls section and nothing would be lost, it should be there instead.
In the previous design, the background color of the BINGO card heading was red. I liked that. Let’s restore that from the original design.
“In the previous design” establishes a before/after reference. “I liked that” confirms the red heading was intentional, not accidental. “Restore from the original design” makes the goal unambiguous: recovery, not redesign.
This is how to report a regression: say what it was, confirm you want it back, and make clear it was introduced by the renovation rather than something you removed intentionally. No ambiguity about whether this is a new feature request or a bug fix.
The red header disappeared because Bootstrap 5.3 changed how table cell backgrounds work. In Bootstrap 5.0.2, setting background-color on a <thead> element was enough — the color propagated visually to the cells.
In Bootstrap 5.3, the .table class applies background-color: var(--bs-table-bg) directly to every <td> and <th> cell via the rule .table > :not(caption) > * > *. The custom property defaults to transparent, applied per-cell — which overrides any background set on a parent <thead>.
The first fix attempt (!important on the thead class) failed because !important raises specificity on the element it is declared on, but cannot override a property declared directly on a child. The fix must target the cells themselves:
.bg-custom-color1, .bg-custom-color1 td, .bg-custom-color1 th { background-color: #E22200 !important; color: #fff !important; }
Bootstrap 5.3 introduced CSS custom properties into table rendering as an improvement for row-level theming (e.g. --bs-table-bg: #fff3cd on a single <tr>). The side effect is that the old pattern of setting a background on a parent container no longer works — Bootstrap overrides each cell individually.
The rule for Bootstrap 5.3 custom table colors: always target the cells, not the container.
.your-class— the container itself (for border/padding visibility).your-class td— all data cells inside the container.your-class th— all header cells inside the container
In the previous design, the ‘Click to Draw’ panel had been placed in the hero area on the left, with a small opacity to give a see-through effect. I know it was atypical but the benefit was that it was out of the way and allowed full view of the card and the buttons below it. Now, it’s impossible to see the draw button, the card and the buttons under it in one view without vertical scrolling. My view is that this exception to ‘normality’ is worth it; The other solution is to put the drawing control to the left of the card. Your perspective?
This prompt demonstrates four good habits for presenting a design dispute:
- States the original reasoning — “out of the way and allowed full view” explains why the atypical hero placement had real value beyond mere familiarity.
- Names the UX failure precisely — “impossible to see the draw button, the card, and the buttons in one view without scrolling” is a specific, testable failure, not a vague complaint.
- Presents both solutions before asking — proposing the hero placement AND the side-by-side alternative shows the problem has already been thought through. The AI is being asked to analyze, not to discover.
- Explicitly invites a recommendation — “Your perspective?” asks for a position with reasoning. This is the right use of an AI collaborator when you are genuinely uncertain which path is better.
Note: the user’s own instinct (“exception to normality is worth it”) was defensible. The hero placement solved the viewport problem pragmatically. The AI’s job was not to dismiss that reasoning but to offer an alternative that solves the same problem without the semantic trade-off.
Recommendation: side-by-side layout, using Bootstrap’s col-auto + col pattern. The reasoning:
- The scrolling problem and the semantic problem both go away. The cage panel sits to the left of the card at the same vertical level.
- When the cage is hidden (
d-noneon the cage column), Bootstrap recalculates the row automatically. The card expands to fill 100% of the row width — no CSS change needed. - The pattern is already in this ecosystem: the classroomDial uses canvas-left / controls-right in exactly the same way.
- No JavaScript changes required. The JS already toggles
d-noneonbingoCageDiv. MakingbingoCageDivthe Bootstrap column element means the column show/hide is driven by the same existing toggle. One element, one responsibility.
The hero placement had one genuine advantage: the semi-transparent overlay gave the cage a “secondary” visual weight. The narrow cage column achieves the same hierarchy structurally — it is visually subordinate to the wide card column without needing opacity.
The Bootstrap row pattern used here:
col-12 col-md-autoonbingoCageDiv— full-width on mobile (stacks above card), shrinks to content width on medium+ screens (sits beside card)col-12 col-mdon the card wrapper — full-width on mobile, fills remaining space on medium+ screens
col-auto means “size this column to its content.” The cage panel is as narrow as its text and image require. col (no number) means “fill whatever is left.” When d-none is added to the cage column, Bootstrap removes it from the row calculation entirely — the card column becomes the only column and fills 100% of the width.
This is the correct use of Bootstrap’s grid: let the framework manage the layout math. The CSS file did not need a single new rule to make the expand/collapse behavior work.
The cage panel is still far away from the intro area; I’m thinking if we put that panel inside the div#intro, on the left, then they would be close to one another and framed in the center. Let’s try that design.
The Bootstrap column gap, even a small one, reads as separation between two independent elements. Moving the cage inside div#intro creates a fundamentally different visual relationship — the cage and card are enclosed in the same white frame, reading as parts of one component rather than two adjacent things.
Implementation: d-flex align-items-center on #intro makes the cage and table flex siblings. An amber right border on #bingoCageDiv acts as a separator within the shared space. When the cage is hidden (d-none), the table fills the entire white frame — the same zero-JS toggle behavior, now with a simpler HTML structure and no external gap to manage.
The Bootstrap column wrapper was removed entirely. This is an example of the right direction of iteration: try the framework’s structural pattern first, then move to a simpler HTML approach when the structural pattern produces unnecessary distance. The simpler approach is often the better one.
In the original design, headings of various sizes were used to get the correct ‘look’ but were chosen for their sizing. Consequently, there are 5 validation errors surrounding the idea of ‘skipping’ heading levels. Is it better to correct the heading hierarchy and style for sizes or to replace all headings with paragraphs and style those sizings? I’m thinking it’s a poor design choice to use headings based on their size for appearance purposes, right?
“I’m thinking it’s a poor design choice to use headings based on their size for appearance purposes, right?” is a good example of the user arriving at the correct instinct independently and seeking confirmation. This is more productive than asking “which should I do?” without context — it gives the AI a position to affirm or correct rather than a blank slate to fill.
Correct. Replace with styled <p> elements — not because “fixing the hierarchy” is harder, but because fixing the hierarchy would still leave semantically wrong elements in place. Promoting <h4> to <h2> satisfies the validator but doesn’t change the fact that a drawn ball number is not a section heading at any level.
The test for whether an element should be a heading: would a screen reader user want to navigate to this with the “next heading” key? Ball drawn, “Click to Draw!”, “Drawn Entries:”, “Chronological Entries:”, “Sorted Entries:”, “Other controls” — all “no.” Replaced with styled <p> using Bootstrap utility classes (fs-5 fw-bold, fw-semibold small, etc.) to preserve the visual sizing.
The exception: <h2> was used for the offcanvas title (“Welcome to Bingo!”) and the modal title (“Attention!”) because both are genuine dialog headings that title a structural region. They were previously <h1> and <h5> respectively — both wrong. <h2> is correct: one level below the page’s single <h1>.
When any element in a design uses a heading tag for its visual size, ask which bucket it belongs in:
- Heading bucket: titles a section that appears in the document outline; a screen reader user would navigate to it with the heading key; it labels a landmark region (dialog, sidebar). Use the correct level
<h2>–<h6>and style visually with CSS. - Paragraph bucket: a label, instruction, status display, or data output. Use
<p>with Bootstrap sizing utilities (fs-1–fs-6,fw-bold,small) or a custom CSS class to achieve the visual size.
Are there any design/consistency issues in reducing the vertical height of the hero area so that the entire card is visible on the page without scrolling down?
No significant consistency issues. The background image still reads clearly at 140px. All three hero elements (eyebrow, <h1>, subtitle) fit comfortably at the reduced padding (1.2rem 1rem 1rem). The dark overlay, amber border, and TNT navbar are unchanged.
The case for a shorter hero in a game app is actually stronger than in a content or tool app. The classroomDial hero is 260px because the projection context benefits from an extended stage-setting moment before students engage. Bingo is different: the player is both operator and participant, they know what they are here to do, and minimizing the path to the card is a direct UX improvement. The hero’s job is to orient (“this is Bingo”) — not to entertain before the game begins.
Hero height is not a fixed brand element — it is a context-dependent design decision. The guiding question is: how long should the user spend looking at the hero before they interact?
- Projection / presentation tools (classroomDial): longer hero. The teacher sets the stage, students look up. The hero has an audience role.
- Game apps (Bingo, Slot Machine): shorter hero. The player is self-directed. The hero confirms they are in the right place; the card is the destination.
- Content / informational pages (chatlog, news): standard hero. The reader is browsing; the hero can afford to be generous with space.
To wrap up the inclusion of BINGO into TNT, let’s add a card on our js_apps page that links to the Bingo app, as well as entries in the explore page: BINGO is a SPARK app and should be included there, and it is also a Games app and should be on that card, next to the Yahtzee entry. Of course, adding it is ‘news’ so we need an entry there as well. Let’s also update our chatlog to register our link to the chatlog from the app, and our wiring up of the app in the various areas in TNT.
This prompt completes the standard TNT deployment checklist in one message. Every SPARK app goes through the same integration sequence: index grid card, Explore category card, Explore offcanvas listing, and News entry. Doing all four in a single well-scoped prompt avoids the fragmented back-and-forth of “what did we miss?” after each step.
The cross-listing decision — both Games and S.P.A.R.K. — follows the same logic applied to Hot Seat Spinner (Processing + S.P.A.R.K. + Simulations) and Yahtzee (Games + S.P.A.R.K.). An app can be correct in multiple categories; the card and offcanvas ensure it surfaces wherever a user might reasonably look.
Five integration changes across four files:
- Chatlog link in the game — a small
<a href="bingoChatlog.html">with a Font Awesomefa-commentsicon was added below the three game buttons in#controls. Always visible,<small>so it does not compete with the game buttons, and positioned exactly where a curious user looks after noticing the controls. The pattern follows other SPARK apps that have companion documentation. - js_apps.html — new
<figure>added to#appGridbefore the future placeholder. Uses the bingo cage icon from the app’s ownimages/folder, consistent with how other apps reference their icons. - explore.html — Games card — Bingo added as a second featured item alongside Yahtzee. Uses
fa-table-cells(representing the 5×5 card grid) to visually distinguish it from Yahtzee’sfa-dice. - explore.html — SPARK offcanvas + Games offcanvas — Bingo app and chat log added to the S.P.A.R.K. panel (after Conversation Quests Poster, the previous most recent entry); Bingo added to the Games panel (after Yahtzee Zilch).
- news.html — entry #025 added at the top of the 2026 accordion, summarizing the Bootstrap 5.3 table color gotcha, cage panel placement iteration, heading hierarchy vs visual sizing, hero height reasoning, and the pre-existing accessibility fix.
Every new TNT app that reaches production status goes through the same integration sequence. Skipping any step means the app is reachable only by direct URL or accidental discovery:
- Index grid card (JS Apps or Processing Apps) — the icon grid that serves as the primary directory by technology
- Explore category card — featured in the relevant genre; each card is capped at three featured items to stay visually clean
- Explore offcanvas — full listing in every applicable category panel, paired with the chat log where one exists
- News entry — the permanent, searchable record of what was built and why, with direct links
The chatlog link within the app itself is a fifth step specific to SPARK apps: it gives a curious student a direct path from the game to the documented reasoning behind every design decision. The link is intentionally unobtrusive — small text, below the primary game buttons — because not every user needs it, but the student who does should find it without searching.
After deployment, I noticed that the bingo app on a mobile device (portrait orientation) was not rendering in an acceptable fashion. Let’s add a media query such that when the width of the browser is less than 580px, we hide the app panel region that houses the card and the ‘cage’ card and show instead a comment indicating to the user that the app must be in a larger environment and/or in landscape orientation to be played. We have a similar notation in our movie clips.
This prompt is a good example of post-deployment testing as a genuine part of the workflow, not a final afterthought. The user tested on an actual mobile device in portrait orientation — something a desktop browser’s responsive-mode preview approximates but does not fully replicate — and reported the finding concisely. A few things the prompt does well:
- Describes the observed failure, not a guess about the cause — “not rendering in an acceptable fashion” is honest about what was seen; it does not pre-suppose whether the fix belongs in CSS, HTML, or JS.
- References the existing pattern — pointing to the movie clips pages as a working precedent keeps the solution consistent with the codebase and shortens the implementation conversation.
- Scopes the fix precisely — hide the app panel, show a message. No responsive redesign, no new dependencies, no feature creep.
The underlying lesson: Test on real hardware before calling an app “done.” Portrait mobile is a fundamentally different rendering environment than a desktop browser in narrow mode. The bingo card’s 5×5 table, with its 60 px minimum cell widths and width:50% inline constraint, behaves differently on a physical phone than in DevTools. Deploy early to real devices, and add portrait-mobile hardware testing to your personal pre-deploy checklist.
The fix mirrors the TNT Movie Clips pattern (e.g. dearAbby.html). Two changes to bingoIndex.html:
CSS (added to the inline <style> block):
.bingo-mobile-warning—display:noneby default; centered, red, bold, with generous padding so it fills the space attractively.- Media query
@media (max-width: 579px)— makes the warning visible and hides#introDivand#controlswith!importantto override any Bootstrap utility classes already on those elements.
HTML (inside #appSection > .container, above #introDiv):
- A
<p class="bingo-mobile-warning">with a Font Awesomefa-rotateicon and the message: “Bingo requires a larger screen or landscape orientation to play. Please rotate your device or switch to a tablet or desktop.”
The breakpoint is 579 px (one pixel below the specified 580 px) so a browser exactly at 580 px receives the full game. This aligns closely with Bootstrap’s sm breakpoint at 576 px. The game logic in init() runs regardless of viewport size — the card is generated in the DOM whether or not it is visible — so if the user rotates to landscape mid-session, the card they had is immediately visible with no reload required.
Two general approaches exist for a layout that breaks at narrow widths:
- Reflow and adapt — redesign the layout to work at all widths: stack the cage above the card, reduce font sizes, compress the cells. More work; fully responsive; no user excluded.
- Hide and warn — acknowledge that the app requires a minimum usable surface, hide the broken layout, and tell the user clearly what to do instead.
For Bingo, “hide and warn” is the correct choice:
- A 5×5 card has a hard minimum readable size. Below ~580 px portrait, cells are too small for a number to be readable and the touch target to be accurately tappable. A technically functional layout would be a practically unusable one.
- The game also involves an offcanvas sidebar for settings and drawn-ball history. Those interactions compound the problem in portrait view.
- The pattern is already established in the codebase by the movie clips pages. Consistency matters: a student who encounters the same message in two different apps can recognize the pattern and learn from it.
The warning explicitly tells the user what to do: rotate to landscape. This turns “the app is broken” into “the app needs a bit more space — here is how to get it.” That is the difference between a dead end and a helpful boundary.
The Bingo app passed all desktop testing before deployment. The portrait-mobile problem was found only after going live. This is not a failure — it is the expected outcome when portrait-mobile testing is not on the checklist. Here is how to close the gap:
- DevTools is not a real device. The rendering engine, touch-target sizes, default font scaling, and browser-chrome height all differ between a desktop simulator and a physical phone. DevTools responsive mode is useful for quick iteration; it is not sufficient for sign-off.
- Portrait is the default orientation. Most phone users hold their phone in portrait mode. If your app has not been tested in portrait on real hardware, it has not been tested for the most common mobile scenario.
- The fix is five minutes of code. Two CSS rules and one HTML element. There is no reason to ship without it once you know the pattern.
- Add “portrait mobile test on hardware” to your personal deployment checklist alongside HTML validation and link checking. Every app, every time.
- Scope your first pass to structure, not style. The preliminary renovation changed the navbar, hero, footer, and Bootstrap version — all structural. It deliberately left the inline game styles, the JavaScript, and the bingo-specific CSS logic untouched. This is the right order: get the skeleton right first, then refine the surface. Mixing structural changes with style changes in the same pass makes it harder to isolate what broke what when something goes wrong.
- Context determines dark vs light, not personal preference. The classroomDial got a light overlay because it is a projection tool used in lit rooms. Bingo got a dark overlay because it is a game played on personal devices in an energetic context. Both decisions followed from reading the app’s use case, not from aesthetic preference. The design system (dark navbar, dark footer, brand fonts) stays consistent; the app-level palette adapts to context. That is what a design system is for.
- An accessibility bug is a bug.
The
for="check1"mismatch on the offcanvas checkbox label was in the original code. It was invisible in mouse-based testing. The renovation caught it because the HTML was being read carefully as part of the read-first discipline. Accessibility bugs are not cosmetic — they are functional failures for users who navigate by keyboard or use screen readers. Fix them when you see them, document them when you do.
| File | Status | What it does |
|---|---|---|
bingoIndex.html |
Renovated | TNT navbar, compact hero, cage-inside-white-frame layout, dark footer; Bootstrap 5.3.3; heading hierarchy corrected; SEO; linkGuard; portrait-mobile hide-and-warn |
styles/bingoSPARKStyles.css |
New | Compact hero (140px), festive app section, cage flex panel inside white frame, Bootstrap 5.3 cell-targeted color tokens |
bingoChatlog.html |
New | This page — the SPARK development log with prompt critique and design decisions |
styles/bingo-styles.css |
Preserved (not linked) | Legacy styles; kept on disk for reference; to be retired in a future clean-up pass |
All JavaScript in bingoIndex.html |
Unchanged | Card generator, draw mechanics, token toggle, localStorage, offcanvas, modal |
Next steps (future passes): move inline
<style> to bingoSPARKStyles.css; add the Bingo app to the JS Apps
index page; add a news entry; consider win detection logic.