Skip to content

Refactor GoodsTable layout and add readGoodColor utility for dynamic good color retrieval#148

Open
lordscarlet wants to merge 12 commits into
YourDeveloperFriend:mainfrom
lordscarlet:add-colors-to-newcity-characters
Open

Refactor GoodsTable layout and add readGoodColor utility for dynamic good color retrieval#148
lordscarlet wants to merge 12 commits into
YourDeveloperFriend:mainfrom
lordscarlet:add-colors-to-newcity-characters

Conversation

@lordscarlet

@lordscarlet lordscarlet commented Aug 23, 2025

Copy link
Copy Markdown
Contributor

Closes #143

This pull request significantly improves the layout, accessibility, and visual consistency of the GoodsTable component, especially for distinguishing between White and Black groups. The changes introduce new CSS for better grouping and styling, refactor the table rendering logic to use these groups, and add a new HeaderHex component for consistent hex-shaped headers with dynamic coloring. Additionally, a utility is added to reliably read good colors from CSS at runtime.

UI and Layout Improvements:

  • Refactored the GoodsTable to render columns grouped into White and Black sections, each with improved headers and layout, using new CSS classes like groupsGrid, groupHeader, blackColumn, and blackHeader for clearer visual separation and alignment. [1] [2] [3] [4] [5]

Visual Consistency and Accessibility:

  • Introduced the HeaderHex component to render hex-shaped SVG headers for each column, matching the map's style and dynamically coloring the hex using the correct good color, with automatic text color for contrast.
  • Improved column and cell sizing, padding, and alignment to ensure consistent appearance and spacing across both White and Black groups. [1] [2]

Color Handling:

  • Added the readGoodColor utility to dynamically read the --good-color CSS variable for a given good, ensuring that color assignments in the UI always match the CSS source of truth. [1] [2] [3] [4]

Code Organization:

  • Updated imports in goods_table.tsx to include the new color utility and supporting functions for polygon/hex rendering.
image

These changes collectively make the goods table more visually appealing, maintainable, and accessible, while ensuring color consistency with the rest of the application.

@lordscarlet

Copy link
Copy Markdown
Contributor Author

@YourDeveloperFriend, any idea why the tests are failing? I tried a few things, but I don't want to dig in too much on an unrelated change.

@lordscarlet lordscarlet marked this pull request as ready for review August 23, 2025 04:03
Copilot AI review requested due to automatic review settings August 23, 2025 04:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR refactors the GoodsTable layout to improve visual organization and add dynamic SVG hex headers that match map hex colors. The changes enhance the table's visual clarity by separating white and black column groups and ensuring color consistency with the game map.

  • Restructured the CSS grid layout to group white and black columns separately with dedicated headers
  • Added dynamic SVG hex headers that retrieve colors from CSS variables to match map hexes
  • Implemented visual styling for black columns with dark backgrounds and white text

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
src/client/grid/readGoodColor.ts New utility function that reads CSS --good-color variables dynamically with DOM manipulation and caching
src/client/game/goods_table.tsx Major refactor of table layout with grouped columns, HeaderHex component, and dynamic color logic
src/client/game/goods_table.module.css Updated CSS with new grid layout, group styling, and black column visual enhancements

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment thread src/client/grid/readGoodColor.ts Outdated
Comment thread src/client/game/goods_table.tsx Outdated
Comment thread src/client/game/goods_table.tsx Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@lordscarlet lordscarlet marked this pull request as draft August 23, 2025 12:49
Comment thread src/client/game/goods_table.tsx Outdated
// determine a primary good color for this onRoll column
let primaryGood: Good | undefined = undefined;
// first try to find the actual City on the map with this onRoll/group so color matches the map hex
const mapCity = grid.cities().find((c) =>

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this just the city&urbanizedCity above? Why do you need to find it again?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Updated in commit 2c6d0c8: I simplified the logic and removed the extra fallback search path, keeping color selection focused on the intended city mapping for this column.

Comment thread src/client/game/goods_table.tsx Outdated
}
// final fallback: use the goods currently in the city/urbanized city if present
if (primaryGood == null && city != null) {
for (const g of city) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this? This is weird. If the city has no color, then you should have a colorless fallback. See Alabama for an example.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in commit 2c6d0c8 by removing the weird fallback path and keeping a clear fallback behavior in HeaderHex (defaultColor) when no city color is available.

Comment thread src/client/game/goods_table.tsx Outdated
if (primaryGood == null && urbanizedCity != null) {
for (const g of urbanizedCity) {
if (g != null) {
primaryGood = g as Good;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this urbanizedGood, the thing you're doing below?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, agreed. Updated in commit 2c6d0c8 so letter color handling is separated and explicit: letterGood comes from availableCities for A..H, and falls back to primaryGood when absent.

Comment thread src/client/game/goods_table.tsx Outdated
const fillColor = primaryGood != null ? readGoodColor(primaryGood) : defaultColor;
const goodClass = primaryGood != null ? goodStyle(primaryGood) : "";

function parseHexOrRgb(color: string): [number, number, number] {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move these functions to the module level? That way we can guarantee you're not mixing variables from the function itself.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit 2c6d0c8. I moved parseHexOrRgb and luminance to module scope so they are no longer defined inside HeaderHex.

Comment thread src/client/grid/readGoodColor.ts Outdated
@@ -0,0 +1,26 @@
import { Good } from "../../engine/state/good";

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use the filename read_good_color to match the convention of the project.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit 2c6d0c8. Renamed readGoodColor.ts to read_good_color.ts and updated imports accordingly.

@lordscarlet

Copy link
Copy Markdown
Contributor Author

@YourDeveloperFriend I will get back to this, btw -- I had a lot of work and other distractions the past couple of months and dropped off. But I also think this is a fairly low priority as well, and I may dabble in another issue before coming back to it, if you don't mind.

…Color, improve type safety, and refactor logic

- Rename readGoodColor.ts to read_good_color.ts to match project conventions
- Optimize readGoodColor to use persistent hidden element instead of creating/removing DOM elements for each call
- Extract parseHexOrRgb and luminance functions to module level to avoid closure issues
- Fix type safety: replace 'any' types with MutableAvailableCity import and proper typing
- Simplify letter good color logic: clarify separation between primaryGood (from map) and letterGood (from availableCities)
- Remove unused goodClass from HeaderHex SVG
- All tests passing
- Remove 'White' / 'Black' section headers (distinguished by background only)
- Change black-dice background from dark grey (#333) to warm brown (#5d4037)
- Apply brown background to entire black section (continuous, not per-column)
- Replace SVG hex letter headers with tab-shaped badges matching physical board
- Remove empty placeholder slots for columns without urbanized cities
- Use Roboto sans-serif font for all table headers
- Ensure all text meets WCAG AA contrast (4.5:1 minimum):
  - White-side numbers: YourDeveloperFriend#222 on white (~17.6:1)
  - Black-side numbers: #fff on #5d4037 (~9.4:1)
  - Letter badges: auto-selected via chooseBestTextColor()
- Remove unused polygon import and orphaned CSS classes
@lordscarlet

lordscarlet commented Mar 5, 2026

Copy link
Copy Markdown
Contributor Author

Updated the Goods Growth Table based on the design discussion in #143.

Changes

  • Removed "White" / "Black" section headers — the two dice groups are now distinguished by background color alone, saving vertical space (per SirLinkan's point Replace current feedback submissions with a link to Github issues #1)
  • Changed black-dice background from dark grey (#333) to warm brown (#5d4037), closer to the physical board's brownish color (per SirLinkan's point [General] Create HotSeats #2)
  • Applied brown background to entire black section as one continuous backdrop rather than per-column
  • Replaced SVG hex letter headers with tab-shaped badges matching the physical board's tab indicators
  • Removed empty placeholder slots for columns without urbanized cities (white onRoll 1-2, black onRoll 5-6) — these spots don't exist on the physical board (per SirLinkan's point Add more user information to the game log. #3)
  • Used Roboto sans-serif font for all table headers, consistent with the rest of the app
  • Kept number headers as plain text (black on white side, white on brown side) without colored backgrounds, matching the physical board

WCAG AA Compliance

All text in the table meets WCAG AA contrast guidelines (minimum 4.5:1 ratio):

Element Foreground Background Contrast Ratio
White-side numbers #222222 white ~17.6:1
Black-side numbers #ffffff #5d4037 ~9.4:1
Letter badges (red/purple/black fills) #ffffff varies >=4.5:1
Letter badges (yellow/blue fills) #222222 varies >=5.8:1

The chooseBestTextColor() utility automatically selects white or dark text for each letter badge based on which provides better WCAG contrast against the fill color.

image image

…ble and improve code quality

Critical fixes:
- File already correctly named read_good_color.ts (snake_case convention)
- Add bounds checking for availableCities array access to prevent runtime errors
- Remove redundant grid.cities().find() by caching City objects in useMemo
- Replace magic numbers with named constants (TOTAL_COLUMNS, WHITE_COLUMNS, etc.)

Code quality improvements:
- Extract column generation to createGoodsColumn helper function for better separation
- Add memoization for column generation with proper dependencies
- Add development-mode error logging in readGoodColor with ESLint pragma
- Improve type safety throughout

All changes follow established codebase patterns and conventions. Addresses unresolved owner comments regarding redundant lookups and code organization.
…nit tests

Quality improvements:
- Add cleanupGoodColorReader() export for proper resource cleanup and testing
- Replace hardcoded color #5d4037 with --black-group-bg CSS variable
- Replace hardcoded #e69074 with COLORLESS_DEFAULT constant referencing CSS
- Add comprehensive unit tests for readGoodColor utility

Testing:
- Create read_good_color_test.ts with Jasmine tests
- Test caching behavior, multiple goods, and cleanup functionality
- Follow established test patterns from codebase

Theme consistency:
- Define CSS variables in goods_table.module.css
- Document constant-to-CSS-variable relationship for maintainability

All enhancements follow established patterns and pass TypeScript/ESLint checks.
@lordscarlet lordscarlet marked this pull request as ready for review March 5, 2026 16:37
@lordscarlet

lordscarlet commented May 2, 2026

Copy link
Copy Markdown
Contributor Author

Dark-mode follow-up on this branch is complete and verified.

What changed:

  • Made Goods Table roll-number text theme-aware (removed hardcoded inline color, now CSS-driven).
  • Added dark-mode-specific background treatment for white/black grouped columns to improve separation and readability.

Validation:

  • No diagnostics errors in touched files.
  • Manually verified in browser on this branch: dark mode looks good.

@lordscarlet

lordscarlet commented May 3, 2026

Copy link
Copy Markdown
Contributor Author

Pulled latest main into this branch, which brought in the new unified Checks workflow (Build/Test/Format/Lint).

I then updated the branch to satisfy those new gates:

  • fixed formatting issues reported by the new Format job
  • addressed the test-time CSS module loading issue so Test can compile/run cleanly
  • re-ran lint/tests locally and pushed the fixes

Result: this branch has been updated specifically to pass the new CI checks introduced from main.

@JackOfMostTrades JackOfMostTrades left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two broad comments on this one:

  1. While this is very much a matter of personal sense of aesthetics and taste, I feel like the amount of contrast between the white background and the dark brown background in light mode is a lot. Whereas in dark mode, there's not nearly so much contrast between the nearly-black left side and the dark brown right side. Could you see if there's a lighter color background for right side in light mode that's less of a stark contrast? I'd be inclined to go so far as to make it a color that's light enough that white text headers would still be appropriate.
  2. It feels like there's a lot of new code needed now to render the goods table... could these color choices just be calculated one time (maybe using a one-off script checked into the scripts dir) and encoded into the CSS and class names used when rendering the table? And have a pre-computed lookup table of Good => dark/light text?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add colors to NewCity characters

4 participants