Skip to content

Eop card browser/m4 eop styling config - #3315

Open
ayman-art wants to merge 24 commits into
masterfrom
eop-card-browser/m4-eop-styling-config
Open

Eop card browser/m4 eop styling config#3315
ayman-art wants to merge 24 commits into
masterfrom
eop-card-browser/m4-eop-styling-config

Conversation

@ayman-art

@ayman-art ayman-art commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Description

  • EoP's suit colors and card artwork were hardcoded in the code. This PR moves them into YAML config files instead, read by two new services (SuitStylingService, CardImagesService).
  • Adds a new EopCard component that draws EoP cards with their own look (background artwork, watermark number, color tab), since EoP cards look different from the other decks' cards. This component is now used on every page that shows a card (/cards, /cards/[card], /edition/[edition]/...).
  • Fixes four EoP card images that had a white background instead of transparent.
  • Completes milestone M4 of the EoP Card Browser Project Adding a endpoint for each eop card and to the card browser #1322

Changes

Feature 1: EoP suit styling and card images now come from YAML files

  • source/eop-styling-5.0.yaml: suit colors (tab/watermark/royal)
  • source/eop-card-images-5.0.yaml: image path for each card
  • src/lib/services/suitStylingService.ts / src/lib/services/cardImagesService.ts: read and cache this data
  • src/routes/+layout.server.ts: loads this data for every page. It now checks every deck, not just EoP, for a matching yaml file, so a future deck can add its own file with no code changes needed. Decks without a file (all except EoP today) are simply skipped, no error.
  • src/lib/services/yamlDataLoader.ts: shared code both services now use, since they were doing almost the same thing (flagged by qlty as duplicate code)

Feature 2: Card style picked automatically in cardPreview.svelte

  • src/lib/components/cardPreview.svelte: draws EoP's card look (image, watermark, colored tab, royal color) directly, instead of using a separate EopCard file. To decouple the code from data, it checks if there's image data for that deck instead of checking if the deck is named 'eop'. This means a future deck with the same structure needs no code change here.
  • src/lib/components/eopCard.css: EoP's own styling (sizes, colors) for this card look. No longer imported by hand. cardPreview.svelte loads it automatically using import.meta.glob('./*Card.css', { eager: true }), so any {edition}Card.css file is imported on its own.
  • src/lib/components/cardBrowser.svelte, cardFound.svelte: pass the card images/colors down to cardPreview.svelte

Feature 3: Card image fixes

  • Fixed the four EoP card images that had a white background instead of transparent, using the open-source GIMP editor, and removed their opaque tags

Feature 4: Tests

  • src/lib/services/cardImagesService.test.ts / suitStylingService.test.ts: added tests for cache-hit and parse-failure cases, removed a redundant async from two synchronous test cases

Notes

AI Tool Disclosure

  • My contribution does not include any AI-generated content
  • My contribution includes AI-generated content, as disclosed below:
    • AI Tools: [e.g. GitHub CoPilot, ChatGPT, JetBrains Junie etc.]
    • LLMs and versions: [e.g. GPT-4.1, Claude Haiku 4.5, Gemini 2.5 Pro etc.]
    • Prompts: [Summarize the key prompts or instructions given to the AI tools]

Affirmation

ayman-art and others added 9 commits August 2, 2026 10:29
Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>
Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>
Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>
Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>
Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>
Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>
Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>
… removed 'opaque' tags

Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>
@qltysh

qltysh Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

All good ✅

Comment thread cornucopia.owasp.org/src/lib/services/cardImagesService.ts Outdated
Comment thread cornucopia.owasp.org/src/lib/services/cardImagesService.ts Outdated
Comment thread cornucopia.owasp.org/src/lib/services/suitStylingService.ts Outdated
Comment thread cornucopia.owasp.org/src/lib/services/suitStylingService.ts Outdated
@ayman-art

Copy link
Copy Markdown
Collaborator Author

Hi @rewtd, @sydseter
Here is a summary of what I did, and what is next:

What's done

  • Added eop-styling-5.0.yaml and eop-card-images-5.0.yaml to hold suit colors and per-card image paths, instead of hardcoding them in CSS.
  • suitStylingService.ts and cardImagesService.ts load them.
  • Updated +layout.server.ts to read EoP's data and pass it down through the existing routes and components.
  • Extracted the EoP-specific card into its own eopCard.svelte and eopCard.css.

What's configurable now

  • Colors
  • Images

what else should be configurable?

Per-image positioning (Didn't need it since the images fit well without handling each card image positioning, but that's not guaranteed for future decks.)
Card geometry (tab size/position, ...)

What do you think?

@sydseter

sydseter commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Hi @rewtd, @sydseter Here is a summary of what I did, and what is next:

What's done

  • Added eop-styling-5.0.yaml and eop-card-images-5.0.yaml to hold suit colors and per-card image paths, instead of hardcoding them in CSS.
  • suitStylingService.ts and cardImagesService.ts load them.
  • Updated +layout.server.ts to read EoP's data and pass it down through the existing routes and components.
  • Extracted the EoP-specific card into its own eopCard.svelte and eopCard.css.

What's configurable now

  • Colors
  • Images

what else should be configurable?

Per-image positioning (Didn't need it since the images fit well without handling each card image positioning, but that's not guaranteed for future decks.) Card geometry (tab size/position, ...)

What do you think?

This sounds good. I'll have a look

@OWASP OWASP deleted a comment from github-actions Bot Aug 3, 2026
@OWASP OWASP deleted a comment from github-actions Bot Aug 3, 2026
@OWASP OWASP deleted a comment from github-actions Bot Aug 3, 2026
@OWASP OWASP deleted a comment from github-actions Bot Aug 3, 2026
@sydseter

sydseter commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

@ayman-art have a look at the review comments from qltysh.

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.

🟡 Not ready to approve

The new EopCard component can render with an undefined card (and calls eopValue with possibly undefined inputs), which can lead to incorrect output/TypeScript errors and should be guarded/fixed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

This PR adds YAML-backed styling and per-card image configuration for the EoP (Elevation of Privilege) edition in the Svelte site, and wires that configuration through layout data into the card preview/browser UI.

Changes:

  • Added new source/ YAML files for EoP suit styling (colors) and card image paths (per card ID).
  • Introduced CardImagesService and SuitStylingService (with Vitest coverage) and exposed their data via +layout.server.ts.
  • Refactored EoP rendering in CardPreview into a dedicated EopCard component and passed cardImages/suitStyling through relevant routes/components.
File summaries
File Description
source/eop-styling-5.0.yaml Adds suit color configuration for EoP 5.0.
source/eop-card-images-5.0.yaml Adds per-card image path mapping for EoP 5.0.
cornucopia.owasp.org/src/routes/edition/[edition]/+page.svelte Passes card image + styling data into CardPreview for edition view.
cornucopia.owasp.org/src/routes/edition/[edition]/[card]/+page.svelte Passes card image + styling data into CardFound.
cornucopia.owasp.org/src/routes/edition/[edition]/[card]/[version]/+page.svelte Passes card image + styling data into CardFound.
cornucopia.owasp.org/src/routes/edition/[edition]/[card]/[version]/[lang]/+page.svelte Passes card image + styling data into CardFound.
cornucopia.owasp.org/src/routes/cards/+page.svelte Passes card image + styling data into CardPreview for cards index.
cornucopia.owasp.org/src/routes/cards/[card]/+page.svelte Passes card image + styling data into CardFound for card detail.
cornucopia.owasp.org/src/routes/+layout.server.ts Loads EoP card images + suit styling in the root layout.
cornucopia.owasp.org/src/lib/services/suitStylingService.ts New service to load and cache suit styling YAML.
cornucopia.owasp.org/src/lib/services/suitStylingService.test.ts Tests for suit styling loader behavior.
cornucopia.owasp.org/src/lib/services/cardImagesService.ts New service to load and cache card images YAML.
cornucopia.owasp.org/src/lib/services/cardImagesService.test.ts Tests for card images loader behavior.
cornucopia.owasp.org/src/lib/components/eopCard.svelte New EoP card rendering component using styling + images.
cornucopia.owasp.org/src/lib/components/eopCard.css Extracted CSS for the EoP card renderer.
cornucopia.owasp.org/src/lib/components/cardPreview.svelte Uses EopCard instead of inline EoP markup/styles; adds props for images/styling.
cornucopia.owasp.org/src/lib/components/cardFound.svelte Plumbs image/styling props into CardBrowser.
cornucopia.owasp.org/src/lib/components/cardBrowser.svelte Plumbs image/styling props into CardPreview.
Review details
  • Files reviewed: 18/26 changed files
  • Comments generated: 3
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread cornucopia.owasp.org/src/lib/services/cardImagesService.test.ts Outdated
Comment thread cornucopia.owasp.org/src/lib/services/suitStylingService.test.ts Outdated
Comment thread cornucopia.owasp.org/src/lib/components/eopCard.svelte Outdated

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.

Is this generated? Could this be generalised to be applicable to the entire deck instead of being a separate component?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This was originally inline in cardPreview.svelte, handling the EoP branch alongside the default layout. Since EoP's layout (artwork + watermark + tab) is structurally different, the idea was to extract it into its own file instead of growing cardPreview.svelte. Do you think this is redundant work? Would you rather it stayed inline?

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.

It's not really an improvement. If you create specific ts files whose only purpose is to support the edition, then you make the coupling between code and data stronger. You need to generalise the code so that it can be used for any of the editions. The code can't really refer to the data if you want to add new editions without having to change the code.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks for clarifying! Yes, I want to make sure there's a decoupling between code and data. I merged eopCard.svelte back into cardPreview.svelte, and replaced the card.edition === 'eop' check with a generalized one:

let editionCardImages = $derived(cardImages?.[card?.edition ?? '']);
{#if card && editionCardImages}

It now picks the layout by checking if image data exists for that edition, not by its name. I also removed the hardcoded eop class/CSS variables so nothing in the logic refers to that specific edition anymore.

I also switched the CSS import to:

import.meta.glob('./*Card.css', { eager: true }); // example: eopCard.css

This automatically includes any new deck's CSS, no manual import needed for future decks.

So adding a new deck now only requires:

  • {edition}-styling-{version}.yaml -> suit colors
  • {edition}-card-images-{version}.yaml -> card images
  • {edition}Card.css (its own layout styling) -> automatically imported using this new import line.

No changes to cardPreview.svelte or any other code file needed!

Would like to know your thoughts about this!

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.

This is great. Yes, perfect.

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.

Make sure to the capabilities are documented in the readme.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

README is updated now.

Comment thread cornucopia.owasp.org/src/routes/+layout.server.ts Outdated
@sydseter

sydseter commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

@ayman-art have a look at mine and Copilot's comments as well.

Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>
@ayman-art

Copy link
Copy Markdown
Collaborator Author

@ayman-art have a look at mine and Copilot's comments as well.

Thanks for your review. I have addressed yours, Copiot's, and qlty's comments. Ready for another look!

@ayman-art
ayman-art marked this pull request as ready for review August 4, 2026 01:32
@ayman-art
ayman-art requested a review from sydseter August 4, 2026 01:32
@OWASP OWASP deleted a comment from github-actions Bot Aug 4, 2026
@OWASP OWASP deleted a comment from github-actions Bot Aug 4, 2026
@OWASP OWASP deleted a comment from github-actions Bot Aug 4, 2026
@OWASP OWASP deleted a comment from github-actions Bot Aug 4, 2026
@OWASP OWASP deleted a comment from github-actions Bot Aug 4, 2026
Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>

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.

🟡 Not ready to approve

The new YAML loader currently caches missing-root-key results silently (masking config errors), and the root layout now ships full image/styling maps to every route which has avoidable payload/build-size impact.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Review details

Suppressed comments (3)

cornucopia.owasp.org/src/lib/services/yamlDataLoader.ts:25

  • Building the YAML path via string interpolation hard-codes POSIX separators and bypasses path normalization. Using path.join(...) avoids cross-platform path issues and makes the intent clearer.
        const file = `${__dirname}/../source/${edition}-${this.fileSuffix}-${version}.yaml`;

cornucopia.owasp.org/src/lib/services/yamlDataLoader.ts:34

  • yaml.load(...) is cast as Record<string, T>, but if the YAML is valid yet missing the expected root key (e.g. suits/cards), data becomes undefined and is still cached, silently masking configuration errors and preventing a later retry. This should validate the parsed structure and only cache when the expected key exists.
            const yamlData = fs.readFileSync(file, 'utf8');
            const parsed = yaml.load(yamlData, { schema: yaml.FAILSAFE_SCHEMA }) as Record<string, T>;
            const data = parsed[this.rootKey];
            this.cache.push({ edition, version, data });
            return data;

cornucopia.owasp.org/src/routes/+layout.server.ts:43

  • +layout.server.ts runs for every page, and its returned data is serialized to the client (and duplicated into each prerendered route). Returning the full cardImages/suitStyling maps here will increase payload and build output size even on pages that never render cards; consider moving this to a nested +layout.server.ts under /cards and /edition (or a dedicated endpoint) so only card pages pay the cost.
        fallbackTranslation: event.locals.fallbackTranslation,
        lang: event.locals.lang,
        cardImages: getCardImagesByEdition(),
        suitStyling: getSuitStylingByEdition()
  • Files reviewed: 18/26 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>
@ayman-art
ayman-art force-pushed the eop-card-browser/m4-eop-styling-config branch from 3e62bda to c4fa548 Compare August 4, 2026 15:11
@OWASP OWASP deleted a comment from github-actions Bot Aug 4, 2026
@OWASP OWASP deleted a comment from github-actions Bot Aug 4, 2026
@OWASP OWASP deleted a comment from github-actions Bot Aug 4, 2026
@OWASP OWASP deleted a comment from github-actions Bot Aug 4, 2026
@OWASP OWASP deleted a comment from github-actions Bot Aug 4, 2026
@OWASP OWASP deleted a comment from github-actions Bot Aug 5, 2026
@OWASP OWASP deleted a comment from github-actions Bot Aug 5, 2026
@sydseter

sydseter commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

@ayman-art Question, are you waiting for a new review? Have you looked at all the comments?

@ayman-art

Copy link
Copy Markdown
Collaborator Author

@ayman-art Question, are you waiting for a new review? Have you looked at all the comments?

Thanks for the question. There is Copilot's last comment remaining, once I address it, I will request another review!

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.

4 participants