Building Mason’s Nerdy Icon Showcase
From a CodeHS screenshot to a retro greenbar code showcase —
CSS counters, nth-child stripes, and preserving a student’s authentic voice.
This log documents Stage 1 of the Mason’s Nerdy Icon project: building a showcase page
for a Python Turtle Graphics assignment. The CSS teaching focus is the retro greenbar code display
— a technique that uses nth-child() selectors, counter-reset / counter-increment,
and ::before pseudo-elements to produce a self-maintaining line-numbered, alternating-stripe code block
with zero JavaScript.
Watch for Prompt Critique boxes (amber) and Design Decision boxes (green). The most important lesson here is not any individual CSS property — it is the principle that visual design choices should be defensible: you should be able to explain why a lighter tone, a retro paper metaphor, or a preserved typo serves the content better than the alternative.
I’m wanting to create an app, MasonsNerdyIconUpgrade as a styl’n type app like we did in the convQuestPoster.html. In this app, we are showing Mason’s Python Turtle Graphics code from CodeHS along with the image it produced and we will be improving it via Claude’s magic. To start, let’s create masonsNerdyIconOriginalCode.html with masonsNerdyIconOriginalCodeStyles.css. I think the page should be a lighter tone to reflect the happy tone of the artwork. Mason’s code should show line numbers and be printed in monospaced font. It would be cool if you could style its container with a retro old-school computer paper look with the alternating faint green lines.
This prompt contains four distinct design requirements that pull in complementary directions:
- “styl’n type app like convQuestPoster.html” — establishes the template: TNT navbar, SPARK bar, compact hero, main content card, footer. Structure is already decided.
- “lighter tone to reflect the happy tone of the artwork” — most TNT pages are dark-themed. This explicitly reverses that convention and links the visual design to the content’s emotional register. The nerd emoji is cheerful; the page should be too.
- “line numbers” — a CSS technique requirement, not just an aesthetic preference. How you achieve line numbers (CSS counters vs. JavaScript vs. a library) determines what the page teaches.
- “retro old-school computer paper look with alternating faint green lines” — the most evocative constraint. The user knew exactly what they wanted visually. This unlocks three CSS techniques in one:
nth-child(),counter-reset, and::before.
Almost every TNT page uses a dark or dark-neutral background. This page explicitly inverts that convention. The decision is justified by the content: the artwork is a cheerful nerd emoji; the programmer is a student who spent three hours on something difficult and finished it. The page should feel like a celebration, not a technical document.
The color choices encode this: #fffef5 (warm cream, not clinical white), #f4c430 (golden yellow, energetic and optimistic), and #010256 (Mason’s own bgcolor, authentic) as the accent navy. The page background uses Mason’s navy only in the navbar and hero overlay — everywhere else, it recedes in favor of the warm cream.
The lighter tone also creates a visual contrast that makes the output image stand out: the dark navy emoji frame against the light cream page creates a spotlight effect that draws the eye to Mason’s artwork immediately.
The classic computer-paper look — alternating green and white bands, monospace font, line numbers in a left gutter — is not just decoration. It teaches three CSS techniques simultaneously that appear in professional stylesheets everywhere:
Technique 1: nth-child(6n±) for group alternation
The classic greenbar paper alternated every 3 lines, not every line. Achieving that in CSS requires selecting groups of three with modular arithmetic. :nth-child(6n+1), :nth-child(6n+2), and :nth-child(6n+3) select lines 1, 2, 3, 7, 8, 9, 13… The pattern repeats every 6, covering both halves of each group.
Technique 2: counter-reset and counter-increment
/* Step 1: reset counter to 0 on the parent */ ol { counter-reset: ln; } /* Step 2: increment by 1 for every child */ ol li { counter-increment: ln; } /* Step 3: display the value in ::before */ ol li::before { content: counter(ln); }
This pattern auto-generates the numbers 1, 2, 3… for every <li> element — including blank lines — without JavaScript. Add a line and the numbers update automatically. Remove a line and they update automatically. It is exactly how CSS-generated ordered list numbers work internally, exposed for custom use.
Technique 3: ::before as a self-maintaining gutter column
The line number lives in a pseudo-element, not in the HTML. That means it cannot be selected with Ctrl+A, it cannot be accidentally edited, and it does not appear in clipboard paste. user-select: none reinforces this. The gutter is visual infrastructure, not content — and the CSS keeps them categorically separate.
The dark bar above the code block with the three traffic-light dots (red, yellow, green) and the filename establishes visual context before the code is read. It signals: this is running code, in a specific file, on a specific platform.
The three colored circles are the macOS window control metaphor — instantly recognizable to any student who has seen a coding tutorial screenshot. Including them without any functionality is a design pattern well worth naming: affordance borrowing. You borrow the visual language of a familiar object to communicate context. The circles say “code editor” before a single character is read.
The first two lines of Mason’s code are comments:
# i did this all by myself without an ai or outward assistents
# it took me like three hours
Nothing was corrected. “assistents” is Mason’s spelling. “cricle2 end” (line 24) is Mason’s typo. Both are displayed exactly as written, in the code block and quoted in the intro card.
The reasoning: these details are not mistakes that need fixing — they are evidence of real human work. A student who spent three hours on something and then wrote it down in their own voice produced something authentic. Correcting the spelling would erase that authenticity. The showcase page honours the original, imperfections included. That is itself a teachable principle: when you display someone’s original work, your job is to present it, not improve it.
The output image frame uses background: #010256 — exactly Mason’s bgcolor("#010256") from line 4 of his code. This means two things:
- When the image loads: the frame color matches the canvas background exactly, making the image appear seamless inside the frame.
- When the image does not load yet: the frame shows the navy canvas color that Mason intended, giving students an accurate preview of the environment before the image appears.
This is the same principle as the background-color fallback for hero images: design for both states (image present and image absent) so neither state looks broken.
| File | Status | Notes |
|---|---|---|
masonsNerdyIconOriginalCode.html |
Created | Main showcase page: hero, intro card, retro code display, output image, next-steps card |
styles/masonsNerdyIconOriginalCodeStyles.css |
Created | Full CSS: greenbar stripes, CSS counters, terminal header, light cream palette, print styles |
masonNerdyIconChatlog.html |
Created | This page |
images/masonHero.jpg |
Pending — user provides | Hero section background art. Page falls back to navy gradient without it. |
assets/masonNerdOutput.png |
Pending — save CodeHS screenshot | Mason’s turtle graphics output image. Frame shows #010256 navy while pending. |
- Design the page’s tone to match the content’s emotional register. A dark, serious page for cheerful student artwork sends the wrong signal. The light cream background, warm golden accent, and open layout tell the student: your work is celebrated here. Tone is a design decision, not just an aesthetic one.
- CSS counters and
nth-child()together eliminate JavaScript for line-numbered code displays. The combination ofcounter-reseton the parent,counter-incrementon each child, andcontent: counter()in::beforeproduces an auto-maintained, selection-proof line number gutter. Groups of three alternating colors requirenth-child(6n±)selectors. Both techniques appear in real-world code and are worth practising on a meaningful example like this one. - Preserve original work exactly, including typos. When showcasing a student’s code, your job is to present it, not to improve it. “assistents” and “cricle2” are part of Mason’s authentic voice. Correcting them would make the code tidier and less real. Authenticity is worth more than correctness when the context is celebration, not instruction.
- Match the output frame background to the program’s own background colour.
background: #010256on the image frame is not arbitrary — it is Mason’sbgcolor()value lifted directly from line 4 of his code. The frame becomes correct even before the image loads. Design the loading state, not just the loaded state.
Can we add a Copy button to the dark terminal header bar? I want students
to be able to click it and paste Mason’s code straight into CodeHS without selecting
it all manually. Also — if we put a button inside that header, I think the
aria-hidden on the whole container is going to be a problem. Can we fix
that at the same time?
A Copy button was added to the dark terminal header bar, after the
“Python 3 · CodeHS Turtle” label. Clicking it copies all 154 lines of Mason’s
code to the clipboard, ready to paste directly into CodeHS. The implementation uses
navigator.clipboard.writeText() with an execCommand fallback for older browsers,
and converts blank lines to proper empty strings so Python parses the pasted code cleanly.
The aria-hidden="true" attribute was moved from the container
<div> to its three decorative children (traffic-light dots, filename, tag).
An interactive element cannot live inside an aria-hidden region — the button
must be reachable by assistive technology.
On success the button label briefly reads Copied! in green before reverting — the same two-second feedback pattern used across TNT’s Cross Training concept pages.
1. navigator.clipboard.writeText() is Promise-based and context-gated.
It works only on HTTPS or localhost. The .then() / .catch()
chain means a denied or unavailable Clipboard API silently falls through to the
execCommand fallback instead of breaking the UI entirely.
2. The execCommand('copy') fallback pattern.
Create a temporary <textarea>, set its .value, call
.select(), execute execCommand('copy'), then remove the element.
It must exist in the DOM to be selectable — position:fixed; opacity:0
keeps it invisible without removing it from the flow prematurely.
3. arrives in textContent as
the Unicode code point \u00a0.
Python treats a line containing only \u00a0 as non-empty, which causes an
IndentationError when pasted into an interpreter. The
.replace(/\u00a0/g, '') call converts those blank-looking lines to truly
empty strings before writing them to the clipboard.
4. aria-hidden="true" must never wrap an interactive element.
The attribute hides an element and all its descendants from the accessibility tree.
A button inside an aria-hidden region is clickable by mouse but invisible to
screen readers and unreachable by keyboard navigation. The fix: move aria-hidden
to the individual decorative children (traffic-light dots, filename span, tag span) rather
than the containing bar.
tnt-base-styles.css does not define .spark-bar globally —
each page’s own stylesheet is responsible. Because masonsNerdyIconOriginalCodeStyles.css
had no .spark-bar block, the bar was rendering with browser defaults rather than
the themed TNT appearance seen on convQuestPoster.html.
The exact rule set from convQuestPosterStyles.css was copied and
adapted: background: #080415, padding: 0.5rem 1rem, flex layout, and
muted right-side span color. The only change: link color uses var(--mn-yellow)
instead of var(--cq-vision), so Mason’s golden accent drives the bar rather than
the Conversation Quests gold.
The hero’s background-position was changed from
center (equivalent to 50% 50%) to center 30%
— shifting the vertical crop so the focal area of the ComicCon photo sits slightly
above the container’s midpoint rather than dead-center. This change was made directly
by the developer, not via a Copilot prompt — a useful pattern to note: small
single-property visual tweaks are often faster to make directly and then document
here so the value is never treated as arbitrary later.
The CSS two-value background-position percentage model is
worth understanding precisely:
0%— image top edge flush with container top edge.50%— image midpoint aligned to container midpoint (the defaultcenter).100%— image bottom edge flush with container bottom edge.30%— the image’s 30%-from-top point aligns with the container’s 30%-from-top point, cropping more from the bottom than from the top. Useful when the photo subject sits in the upper-middle of the frame.
The same image appears in both the main app’s hero
(masonsNerdyIconOriginalCodeStyles.css) and the chatlog’s inline
<style> block, so each can be tuned to its own container height
independently. There is no single rule that controls both.
| File | What Changed |
|---|---|
masonsNerdyIconOriginalCode.html |
Copy button added to terminal header; aria-hidden moved to decorative children;
copyAllCode() and fallbackCopy() JS functions added |
styles/masonsNerdyIconOriginalCodeStyles.css |
.spark-bar rule set added — mirrors convQuestPosterStyles.css
with var(--mn-yellow) link color; hero background-position
updated to center 30% directly by developer |
masonNerdyIconChatlog.html |
Post-build section added; chat turn for Copy button request added; Programmatic Focus aside box added; Refinement 3 (hero background-position) documented |
For the upgrade, I’d like you to refactor Mason’s code to reflect the idea of using variables and functions to modularize and generalize the program. I’d like sufficient comments (and breadcrumbs) to show the flow of logic and when we copy the code you write for us, we should be able to copy and paste it into the CodeHS environment and it should run and re-create Mason’s artwork, but just in a more organized and professional manner. The original code, as copy and pasted, worked fine in CodeHS. The upgrade should do likewise.
The refactor produced masonsNerdyIconUpgrade.py. Three structural changes
made all the difference:
- Named constants at the top. Every magic number and color string
became a named variable:
FACE_RADIUS = 75,EYE_SPACING = 72.5,BRIDGE_LENGTH = 145,CHEEK_COLOR = “#ed3e4a”. Changing one constant now updates every part of the code that depends on it. - One function per facial feature.
draw_face(),draw_glasses(),draw_eyes(),reposition_to_mouth(),draw_mouth(), anddraw_cheeks()each own their section. The turtle state still flows sequentially between them — no absolute positioning needed. - Inline comments explain intent, not mechanics.
# negative = clockwise quarter turnis more useful than# circle(75, -90). The section dividers (# STEP 2 — draw_face) act as breadcrumbs matching themain()call list.
The main() function is the payoff: seven calls, one per feature,
readable as prose. Students can trace the draw order without reading the functions themselves.
This worked perfectly! I copied the code into CodeHS and got a great replica. The image created was saved in claudesRefactoredNerd.png. Now, I’d like to create a similar page as masonsNerdyIconOriginalCode.html, called claudesRefactoredNerd.html (with claudesRefactoredNerdStyles.css) and interlink the two pages so we can load them in separate browser tabs for comparison/contrast. Let’s also add this prompt, and the former one where we asked for the refactoring, to our chatlog.
Dynamic code display instead of 300+ hand-coded <li> elements.
The refactored Python file is ~170 executable lines plus comments — too long to hand-author as HTML.
Instead, the full source is stored as a JavaScript template literal (var UPGRADE_CODE = `...`)
in the <head>. On load, buildCodeDisplay() splits it on \n
and injects one <li><code> per line into an empty <ol id="codeList">.
The CSS counter/nth-child greenbar rules work identically on dynamically-added elements.
Inline comment detection via character scan. A for loop
scans each line character by character, tracking whether it is inside a "..." string.
The first # found outside a string marks the comment boundary. This correctly handles
"#010256" (a color string containing #) without false positives.
Stage navigation bar. Both pages share a thin dark bar below the spark bar, showing the current stage and linking to the other. This makes the comparison workflow obvious: click the bar, open the other tab, compare.
| File | Action |
|---|---|
masonsNerdyIconUpgrade.py |
Created — the refactored Python source; paste into CodeHS to run |
claudesRefactoredNerd.html |
Created — Stage 2 showcase page; dynamic code display via JS |
styles/claudesRefactoredNerdStyles.css |
Created — same greenbar palette; adds .py-keyword and .stage-nav-bar |
masonsNerdyIconOriginalCode.html |
Modified — stage nav bar added; “What’s Next” card updated with Stage 2 link |
styles/masonsNerdyIconOriginalCodeStyles.css |
Modified — .stage-nav-bar rule added |
images/claudesRefactoredNerd.png |
Asset — CodeHS output screenshot saved by developer after confirming code ran correctly |
I had wrongly assumed that I could comment out any given function and that it would simply not be drawn. However, each function did ‘orientation’ work that was required for the next function. Therefore, they were comingled. I teach my students to always re-orient Tracy to a given state after a function’s ended and to begin with that state for the next stage of artwork. I call this ‘leave no trace-y’ after the scouting quote: “Leave No Trace.” Meaning, Tracy always reverts to a known state on completing a function and starts from that state to do the next function. Let’s make a 3rd version that follows this principle while still making the artwork.
Stage 2’s functions were architecturally dependent on each other’s ending state. draw_glasses() assumed it would start where draw_face() left off. draw_eyes() assumed it would start where draw_glasses() left off. The sequence was a chain: break any link and everything after it drew in the wrong place.
The most visible symptom was reposition_to_mouth() — a dedicated function whose sole job was to retrace the complex path back to a known arc position and sweep around the face to the mouth zone. In a truly independent design that function simply disappears: draw_mouth() calls goto(0, −35) directly.
The principle is named after the scouting rule “Leave No Trace” — a campsite left exactly as you found it. Applied to turtle graphics, the guarantee has three parts: position (an absolute coordinate set with goto()), heading (an absolute direction set with setheading()), and pen state (explicitly lifted with penup() before any goto()). Every function in Stage 3 opens with the same three-call idiom and closes with the same two-call idiom:
The parking spot (PARK_X=0, PARK_Y=−300) doubles as a debugging aid: if a function forgets penup() before exiting, a visible line trails from the last drawing position off the canvas, immediately identifying which function failed to clean up.
Mason’s original backward(50) placed the face center at approximately (−50, 75) in canvas coordinates. For a Leave No Trace-y design, working from an offset center complicates every goto() constant.
Stage 3 eliminates the offset. The face arcs start at goto(0, −75); setheading(0) — the bottom of a circle whose center is exactly (0, 0), the canvas origin. Positions are now clean symmetric offsets from that anchor: mouth center at (0, −35), glasses entry at (72.44, 19.41), cheek dots at (−35, −35) and (45, −35). The math is the same; the bookkeeping is dramatically simpler.
Every goto() constant was derived by tracing the circle geometry of Stage 2’s movement sequence, using the rule that circle(r, extent) places its arc center r units to the left of the current heading and advances the heading by extent degrees. The face center calculation:
For the remaining positions (eye zone, cheek dots), the analytical path through the glasses-frame sequence is complex. The recommended verification method: add print(xcor(), ycor(), heading()) at the end of each Stage 2 function, run once in CodeHS, and compare the numbers to the Stage 3 constants. The full analytical derivation is in leaveNoTraceyNotes.md.
FACE_RADIUSAll Stage 3 coordinate constants are currently hardcoded pixel values derived from Mason’s original geometry (EYE_START_X = 34.0, MOUTH_START_Y = −35, etc.). This is correct for the original drawing at its original size, but the drawing cannot be scaled without recalculating every constant.
The logical next step — should scaling or responsiveness ever become a requirement — is to express every position as a ratio of FACE_RADIUS:
With that structure, changing FACE_RADIUS = 75 to FACE_RADIUS = 150 would scale the entire drawing — face, glasses, eyes, mouth, cheeks — proportionally, with no other changes. FACE_RADIUS derived from canvas dimensions would make the artwork responsive to any canvas size. That is the architecture of a general-purpose drawing function, and it is the design direction Stage 4 would take if pursued.
| File | Action |
|---|---|
claudesRefactoredNerd2.html |
Created — Stage 3 showcase page; STAGE3_CODE template literal; teal color scheme; Leave No Trace-y principle box |
styles/claudesRefactoredNerd2Styles.css |
Created — teal/dark-green theme; same greenbar + terminal-header patterns as Stage 2; adds .lnt-box and .stages-card |
leaveNoTraceyNotes.md |
Created — planning document; analytical coordinate derivation; face center proof; Stage 3 function template; scaling discussion |
claudesRefactoredNerd.html |
Modified — Stage 3 link added to stage nav bar |
images/claudesRefactoredNerd2.png |
Asset pending — run Stage 3 code in CodeHS and save the output screenshot |