Skip to content

Redesign web UI with Warm Studio theme#3

Merged
JuliusScheuerer merged 5 commits into
mainfrom
ux-overhaul
Mar 5, 2026
Merged

Redesign web UI with Warm Studio theme#3
JuliusScheuerer merged 5 commits into
mainfrom
ux-overhaul

Conversation

@JuliusScheuerer
Copy link
Copy Markdown
Owner

@JuliusScheuerer JuliusScheuerer commented Mar 5, 2026

Summary

Complete frontend redesign using an editorial "Warm Studio" aesthetic, replacing the default utilitarian styling with a polished, professional look suitable for an open-source tool.

  • Typography: Instrument Serif + Work Sans font pairing, self-hosted (no external CDN requests)
  • Color palette: Warm amber/terracotta tones with CSS custom properties (--warm-*, --amber-*)
  • Components: Redesigned entity badges (warm pastels per PII type), amber confidence bars, warm card containers, tier sections with collapsible headers
  • Layout: 3px amber gradient accent bar, cream background, refined spacing and shadows
  • Security: CSP headers updated with font-src 'self'; fonts self-hosted to eliminate external requests
  • Housekeeping: CLAUDE.md untracked (personal per-contributor), AI tooling dirs and design artifacts added to .gitignore

All functional JS IDs, CSS classes, data-* attributes, and HTMX behavior are fully preserved — this is a purely visual change.

Screenshots

Before After
Default blue/gray utilitarian UI Warm amber editorial design with serif headings

Test plan

  • All 227 unit tests pass (including new cache-control and middleware tests)
  • Pre-commit hooks pass (ruff, mypy, bandit)
  • Verified via Playwright: computed styles match design (amber buttons, Work Sans body font, Instrument Serif headings)
  • Manual smoke test: upload PDF, detect PII, anonymize, download redacted PDF — confirmed via Playwright: PDF uploaded, 9 entities detected, anonymized with [PERSON]/[IBAN]/etc., redacted PDF downloaded with all PII physically removed
  • Verify fonts load correctly (no CSP violations in console) — confirmed: zero console errors/warnings, self-hosted fonts load without CSP issues

Summary by CodeRabbit

  • Style

    • Redesigned UI with a warm color palette, new typography, updated components, and improved focus/accessibility states.
  • New Features

    • Added a "Load example text" control to quickly populate the input area with sample content.
  • Bug Fixes

    • Normalized line endings for consistent text handling; adjusted caching behavior so static assets are cached and dynamic/PII responses are not.
  • Tests

    • Added tests for line-ending normalization, entity score validation, caching branches, and CSP/font-src.
  • Documentation

    • Removed a legacy project doc and added ignore rules for AI/design artifacts.

Julius Scheuerer added 2 commits March 5, 2026 10:46
Complete frontend overhaul using an editorial design language:
- Instrument Serif + Work Sans font pairing via Google Fonts
- Warm amber/terracotta color palette with CSS custom properties
- Redesigned entity badges, confidence bars, tier sections
- Amber accent header bar, warm card containers, cream background
- CSP updated to allow Google Fonts (fonts.googleapis.com/gstatic.com)

All functional JS IDs, classes, data attributes, and HTMX behavior preserved.
CLAUDE.md is personal per-contributor configuration — each developer
should maintain their own. Also ignore .claude/, .playwright-mcp/,
and design comparison screenshots.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 5, 2026

📝 Walkthrough

Walkthrough

Replaces the Minimal design system with a Warm Studio theme (fonts, tokens, utilities), updates templates and JS to add an example-loader; removes CLAUDE.md and adds .gitignore entries; introduces line-ending normalization and extra debug logging in web routes; adds cache-control branching in middleware and corresponding tests.

Changes

Cohort / File(s) Summary
Repository config
/.gitignore, /.gitattributes
Added ignore entries for AI tooling and design artifacts (CLAUDE.md, .claude/, .playwright-mcp/, design-*.png) and marked *.woff2 as binary.
Removed documentation
CLAUDE.md
Deleted long-form project documentation and contributor guidance.
Design system & CSS
src/document_anonymizer/web/static/css/app.css
Large overhaul: new self-hosted fonts, warm color tokens (--warm-*, --amber-*), many new/renamed utility/component classes and accessibility updates.
Templates (presentation-only)
src/document_anonymizer/web/templates/base.html, .../index.html, .../results.html, .../anonymized.html
Swapped gray utilities for warm-themed classes, added font preloads and minor markup tweaks to apply new styling; no behavioral changes.
Client JS
src/document_anonymizer/web/static/js/app.js
Added example-text loader (load-example-btn) that injects German sample text into the textarea with temporary feedback.
Server routes & logging
src/document_anonymizer/web/routes.py
Added _normalize_line_endings(text) and apply normalization in detect_form and anonymize_form; added granular debug logging for entity-skip conditions; behavior otherwise unchanged.
Security middleware
src/document_anonymizer/security/middleware.py
Conditional Cache-Control: public long TTL for successful /static/ responses, no-store/no-cache for others; unbinds request_id from structlog context in finally.
Tests — web & helpers
tests/test_web/test_review_helpers.py, tests/test_web/test_routes.py
Added tests for line-ending normalization, entity score validation, PII response anti-caching, static asset caching, and CSP font-src assertion.
Tests — middleware
tests/test_security/test_middleware.py
Added test coverage for cache-control branching for static vs. non-static paths and status-code handling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I swapped the grays for amber light,
fonts cozy up and badges bright,
one click loads a sample scene,
logs are clearer, lines kept clean,
hop, review, and ship the sheen. 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'Redesign web UI with Warm Studio theme' directly and clearly summarizes the primary change—a complete frontend visual redesign introducing the Warm Studio aesthetic. The title is concise, specific, and accurately reflects the main objective of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ux-overhaul

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 5, 2026

Greptile Summary

This PR implements a complete visual redesign with "Warm Studio" editorial aesthetics while correctly maintaining privacy guarantees. Critically, all fonts are self-hosted (WOFF2 files in /static/fonts/) with @font-face declarations in app.css, and CSP is correctly left unchanged at font-src 'self' — there are no external CDN requests despite the PR description's mention of "updated CSP headers" (this was stale copy written before the author switched to self-hosting).

Key verifications:

  • Four WOFF2 files (Instrument Serif + Work Sans, latin + latin-ext) are committed and wired up correctly via @font-face and <link rel="preload"> tags in base.html
  • Middleware CSP header remains font-src 'self' with no external font domains
  • Footer claim "No external API calls" is now accurate
  • New _normalize_line_endings helper correctly normalizes CRLF line endings before PII detection/anonymization to prevent entity-offset drift
  • Example-text loader added; purely additive, no security impact
  • All 213 unit tests pass; CSRF, XSS escaping, and HTMX wiring preserved

Two style/quality improvements identified:

  • The clipboard copy fallback uses deprecated document.execCommand("copy"); could be simplified by always showing "not supported" when Clipboard API is unavailable
  • CSP test verifies font-src 'self' presence but lacks negative assertions for external CDN domains; adding assert "fonts.googleapis.com" not in csp would make the privacy invariant machine-enforceable against regressions

Confidence Score: 4/5

  • Safe to merge — purely visual redesign with correctly self-hosted fonts (no external requests) and preserved CSRF/XSS protections; two style improvements worth addressing.
  • All functional logic, HTMX wiring, CSRF headers, and XSS escaping are preserved. Fonts are correctly self-hosted and CSP was not weakened. The two findings are minor style/test-coverage issues that pose no security or correctness risk. The deprecated document.execCommand fallback and missing CSP negative assertions are improvements worth addressing before merge but not blockers.
  • src/document_anonymizer/web/static/js/app.js and tests/test_web/test_routes.py have minor improvements worth addressing before merge.

Last reviewed commit: 1e26bb4

Comment thread src/document_anonymizer/web/templates/base.html Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/document_anonymizer/web/static/css/app.css`:
- Line 133: The font-family declarations use unnecessary quotes around generic
and simple font names; update the declarations that currently read font-family:
'Instrument Serif', 'Georgia', serif; (and the similar occurrences) so that
quoted multi-word family names like 'Instrument Serif' remain quoted but remove
quotes from Georgia and the generic serif/sans-serif keywords (i.e., change to
font-family: 'Instrument Serif', Georgia, serif;), and apply the same change to
the other identical font-family declarations in the file.

In `@src/document_anonymizer/web/templates/base.html`:
- Around line 13-15: Remove the external Google Fonts links from
src/document_anonymizer/web/templates/base.html and replace them with a local
font stylesheet: vendor the Instrument Serif and Work Sans font files into
static/fonts/, create a static/css/fonts.css containing `@font-face` rules for
those families, and update base.html to include a link to /static/css/fonts.css
instead of the fonts.googleapis.com and fonts.gstatic.com URLs so all frontend
assets are served locally.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8ca9a3ce-738b-4c56-81dd-9d3c6b2609ab

📥 Commits

Reviewing files that changed from the base of the PR and between c0f2436 and 27b0c07.

📒 Files selected for processing (8)
  • .gitignore
  • CLAUDE.md
  • src/document_anonymizer/security/middleware.py
  • src/document_anonymizer/web/static/css/app.css
  • src/document_anonymizer/web/templates/anonymized.html
  • src/document_anonymizer/web/templates/base.html
  • src/document_anonymizer/web/templates/index.html
  • src/document_anonymizer/web/templates/results.html
💤 Files with no reviewable changes (1)
  • CLAUDE.md

5. TYPOGRAPHY
============================================================ */
.text-2xl {
font-family: 'Instrument Serif', 'Georgia', serif;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and its size
wc -l src/document_anonymizer/web/static/css/app.css

Repository: JuliusScheuerer/document-anonymizer

Length of output: 129


🏁 Script executed:

# Read the specific lines mentioned in the review
sed -n '130,140p; 648,652p; 672,676p' src/document_anonymizer/web/static/css/app.css

Repository: JuliusScheuerer/document-anonymizer

Length of output: 688


🌐 Web query:

Stylelint font-family-name-quotes rule quoted generic font family names

💡 Result:

Stylelint’s font-family-name-quotes rule will always forbid quoting generic font family keywords (e.g. serif, sans-serif, monospace, system-ui, etc.). The docs explicitly say that if you wrap these keywords in quotes, the browser treats them as a literal font name (e.g. "sans-serif") rather than the generic fallback, so all rule options enforce “no quotes” for these keywords. [1]

So:

  • font-family: "sans-serif";always a lint error with this rule enabled (regardless of option). [1]
  • Correct generic fallback usage: font-family: Arial, sans-serif; (generic unquoted). [1][2]

If you genuinely need a real font family named "sans-serif" (rare), Stylelint recommends you turn this rule off for that case. [1]

Options (affect only non-keyword family names):

  • "always-unless-keyword": quote all non-keyword names. [1]
  • "always-where-required": quote only when CSS syntax requires it. [1]
  • "always-where-recommended": quote when CSS recommends it (e.g., spaces/digits/punctuation), and disallow other quotes. [1]

Sources:
[1] Stylelint rule docs: font-family-name-quotes https://stylelint.io/user-guide/rules/font-family-name-quotes/
[2] W3C CSS2.1 (generic family names are keywords and must not be quoted) https://www.w3.org/Style/css2-updates/css2/css2.pdf


🏁 Script executed:

# Search for stylelint configuration files
fd -e stylelintrc -e stylelintrc.json -e stylelintrc.js -e stylelintrc.yaml -e stylelintrc.yml -e stylelintrc.cjs

Repository: JuliusScheuerer/document-anonymizer

Length of output: 61


🏁 Script executed:

# Also check package.json for stylelint config
cat package.json | grep -A 20 '"stylelint"'

Repository: JuliusScheuerer/document-anonymizer

Length of output: 123


🏁 Script executed:

# Check for .stylelintignore and any stylelint references in config
find . -name ".stylelint*" -o -name "stylelint.config.*" | head -20

Repository: JuliusScheuerer/document-anonymizer

Length of output: 98


🏁 Script executed:

cat .stylelintrc.json

Repository: JuliusScheuerer/document-anonymizer

Length of output: 1085


🌐 Web query:

stylelint-config-standard-scss font-family-name-quotes default option

💡 Result:

In stylelint-config-standard-scss, font-family-name-quotes is not overridden; the config simply extends stylelint-config-standard, so it inherits that setting. In the current stylelint-config-standard, the rule is set to:

  • font-family-name-quotes: "always-where-recommended" (this is the effective/default option you get via stylelint-config-standard-scss). [1], [2]

That option means: quote font-family names only when quoting is recommended (and therefore also when required), and disallow quotes otherwise (keywords like serif, sans-serif, etc. must remain unquoted). [3]

Sources:
[1] https://github.com/stylelint/stylelint-config-standard/blob/main/index.js
[2] https://github.com/stylelint-scss/stylelint-config-standard-scss/blob/main/index.js
[3] https://stylelint.io/user-guide/rules/font-family-name-quotes/


🌐 Web query:

stylelint-config-standard font-family-name-quotes default "always-unless-keyword"

💡 Result:

In stylelint-config-standard, the default setting for font-family-name-quotes is "always-where-recommended", not "always-unless-keyword". This is defined directly in the config’s index.js. [1]

"always-unless-keyword" is a valid option for the rule (per Stylelint’s rule docs), but it’s not what stylelint-config-standard uses by default. [2]

Sources

  1. stylelint-config-standard index.js ('font-family-name-quotes': 'always-where-recommended'). [1]
  2. Stylelint rule docs for font-family-name-quotes (shows "always-unless-keyword" as a supported configuration). [2]

Remove unnecessary quotes from generic font family fallbacks to comply with font-family-name-quotes.

Generic font family keywords (serif, sans-serif, etc.) and single-word font names without special characters should not be quoted. Georgia should be unquoted while 'Instrument Serif' (which contains a space) correctly remains quoted.

🎯 Proposed fix
-  font-family: 'Instrument Serif', 'Georgia', serif;
+  font-family: 'Instrument Serif', Georgia, serif;

Apply this at Lines 133, 139, 650, and 674.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
font-family: 'Instrument Serif', 'Georgia', serif;
font-family: 'Instrument Serif', Georgia, serif;
🧰 Tools
🪛 Stylelint (17.3.0)

[error] 133-133: Unexpected quotes around "Georgia" (font-family-name-quotes)

(font-family-name-quotes)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/document_anonymizer/web/static/css/app.css` at line 133, The font-family
declarations use unnecessary quotes around generic and simple font names; update
the declarations that currently read font-family: 'Instrument Serif', 'Georgia',
serif; (and the similar occurrences) so that quoted multi-word family names like
'Instrument Serif' remain quoted but remove quotes from Georgia and the generic
serif/sans-serif keywords (i.e., change to font-family: 'Instrument Serif',
Georgia, serif;), and apply the same change to the other identical font-family
declarations in the file.

Comment on lines +13 to +15
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Work+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Check if the file exists and view its content
cat -n src/document_anonymizer/web/templates/base.html | head -30

Repository: JuliusScheuerer/document-anonymizer

Length of output: 2164


🏁 Script executed:

# Check the repository structure for static/asset directories
find . -type d \( -name static -o -name assets -o -name fonts \) | grep -v node_modules | head -20

Repository: JuliusScheuerer/document-anonymizer

Length of output: 115


🏁 Script executed:

# Look for any existing font files in the repository
find . -type f \( -name "*.woff*" -o -name "*.ttf" -o -name "*.otf" -o -name "fonts.css" \) | grep -v node_modules

Repository: JuliusScheuerer/document-anonymizer

Length of output: 61


🏁 Script executed:

# Check if there are other HTML/Jinja2 templates that reference static assets to understand the pattern
grep -r "href=\"/static" src/document_anonymizer/web/templates/ --include="*.html" --include="*.j2" --include="*.jinja2" | head -10

Repository: JuliusScheuerer/document-anonymizer

Length of output: 181


External Google Fonts CDN violates vendored assets requirement.

Lines 13–15 introduce fonts.googleapis.com and fonts.gstatic.com dependencies. Per coding guidelines, all frontend assets must be vendored with no external CDN dependencies. Replace with locally served fonts:

🔧 Proposed change
-    <link rel="preconnect" href="https://fonts.googleapis.com">
-    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
-    <link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Work+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
+    <link rel="stylesheet" href="/static/css/fonts.css">

Download Instrument Serif and Work Sans fonts, add to /static/fonts/, and create /static/css/fonts.css with corresponding @font-face rules.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/document_anonymizer/web/templates/base.html` around lines 13 - 15, Remove
the external Google Fonts links from
src/document_anonymizer/web/templates/base.html and replace them with a local
font stylesheet: vendor the Instrument Serif and Work Sans font files into
static/fonts/, create a static/css/fonts.css containing `@font-face` rules for
those families, and update base.html to include a link to /static/css/fonts.css
instead of the fonts.googleapis.com and fonts.gstatic.com URLs so all frontend
assets are served locally.

Replace Google Fonts CDN links with locally served WOFF2 files to
maintain the privacy-first, zero-external-requests promise. Loading
fonts from Google's CDN leaked user IP/UA to third parties on every
page load — contradicting the "No external API calls" footer claim.

- Add @font-face declarations in app.css for Instrument Serif + Work Sans
- Bundle 6 WOFF2 files (latin + latin-ext subsets, ~150KB total)
- Remove Google Fonts <link> and preconnect tags from base.html
- Revert CSP to strict 'self'-only for style-src and font-src
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/document_anonymizer/web/static/css/app.css (1)

188-194: ⚠️ Potential issue | 🟡 Minor

Remove quotes around Georgia to satisfy font-family-name-quotes.

Line 188, Line 194, Line 705, and Line 729 still quote Georgia, which keeps Stylelint failing.

🎯 Proposed fix
-  font-family: 'Instrument Serif', 'Georgia', serif;
+  font-family: 'Instrument Serif', Georgia, serif;

Also applies to: 705-705, 729-729

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/document_anonymizer/web/static/css/app.css` around lines 188 - 194, The
CSS font-family declarations that list 'Georgia' are quoted and trigger the
font-family-name-quotes lint rule; locate the font-family properties in the
shown rule block and the .text-lg selector (and the other occurrences around the
same pattern at the other reported locations) and remove the quotes around
Georgia so the declarations use Georgia (unquoted) alongside the existing quoted
custom family names.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/document_anonymizer/web/static/css/app.css`:
- Line 384: The CSS declaration "border-bottom: 2px solid currentColor;" uses
the mixed-case keyword "currentColor" which violates the project's stylelint
rule value-keyword-case: ["lower"]; update the value to the lowercase keyword
"currentcolor" (i.e., change currentColor to currentcolor) so the rule passes
and the declaration remains equivalent.
- Line 404: Locate the two occurrences of the non-standard CSS declaration
"word-break: break-word" and replace each with the modern property
"overflow-wrap: anywhere;" (remove the old declaration); update the same
selectors that currently contain "word-break: break-word" so they now use
"overflow-wrap: anywhere" for compliant, consistent word-wrapping behavior.

---

Duplicate comments:
In `@src/document_anonymizer/web/static/css/app.css`:
- Around line 188-194: The CSS font-family declarations that list 'Georgia' are
quoted and trigger the font-family-name-quotes lint rule; locate the font-family
properties in the shown rule block and the .text-lg selector (and the other
occurrences around the same pattern at the other reported locations) and remove
the quotes around Georgia so the declarations use Georgia (unquoted) alongside
the existing quoted custom family names.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2ad9a17e-4001-4635-a0f0-cba60b079958

📥 Commits

Reviewing files that changed from the base of the PR and between 27b0c07 and 249a820.

⛔ Files ignored due to path filters (6)
  • src/document_anonymizer/web/static/fonts/instrument-serif-italic-latin-ext.woff2 is excluded by !**/*.woff2
  • src/document_anonymizer/web/static/fonts/instrument-serif-italic-latin.woff2 is excluded by !**/*.woff2
  • src/document_anonymizer/web/static/fonts/instrument-serif-normal-latin-ext.woff2 is excluded by !**/*.woff2
  • src/document_anonymizer/web/static/fonts/instrument-serif-normal-latin.woff2 is excluded by !**/*.woff2
  • src/document_anonymizer/web/static/fonts/work-sans-latin-ext.woff2 is excluded by !**/*.woff2
  • src/document_anonymizer/web/static/fonts/work-sans-latin.woff2 is excluded by !**/*.woff2
📒 Files selected for processing (2)
  • src/document_anonymizer/web/static/css/app.css
  • src/document_anonymizer/web/templates/base.html
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/document_anonymizer/web/templates/base.html

border-radius: 0.25rem;
cursor: help;
cursor: pointer;
border-bottom: 2px solid currentColor;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find lint configuration files
find . -type f \( -name ".stylelintrc*" -o -name "stylelint.config.*" -o -name ".eslintrc*" -o -name "eslint.config.*" -o -name "package.json" -o -name ".lintrc*" \) | head -20

Repository: JuliusScheuerer/document-anonymizer

Length of output: 98


🏁 Script executed:

# Check the actual content around line 384 of the CSS file
cat -n src/document_anonymizer/web/static/css/app.css | sed -n '380,390p'

Repository: JuliusScheuerer/document-anonymizer

Length of output: 409


🏁 Script executed:

# Search for currentColor/currentcolor usage in the CSS file
rg -i "currentcolor" src/document_anonymizer/web/static/css/app.css

Repository: JuliusScheuerer/document-anonymizer

Length of output: 119


🏁 Script executed:

cat .stylelintrc.json

Repository: JuliusScheuerer/document-anonymizer

Length of output: 1085


🌐 Web query:

CSS currentColor keyword casing stylelint keyword-case rule

💡 Result:

In CSS, keyword identifiers like currentColor are case-insensitive, so currentcolor and currentColor both work in browsers; the mixed-case form is commonly shown in docs (e.g., as part of the <color> value set). [2]

In stylelint, casing is enforced by the value-keyword-case rule:

  • If you use "lower", stylelint will normally require currentcolor.
  • If you want to allow/require the SVG-style camelCase spelling currentColor while still enforcing lowercase for other keywords, enable camelCaseSvgKeywords: true: [1]
{
  "rules": {
    "value-keyword-case": ["lower", { "camelCaseSvgKeywords": true }]
  }
}

Alternatively, you can exempt it with ignoreKeywords: ["currentColor"] (or a regex) if you don’t want SVG-specific behavior. [1]

Sources: Stylelint value-keyword-case (including camelCaseSvgKeywords). [1] MDN <color> value (includes currentColor). [2]

[1] https://stylelint.io/user-guide/rules/value-keyword-case/
[2] https://developer.mozilla.org/en-US/docs/Web/CSS/color_value


Change currentColor to lowercase currentcolor.

Line 384 uses currentColor, but the project's lint rules (inherited from stylelint-config-standard via .stylelintrc.json) enforce lowercase keyword values with the value-keyword-case: ["lower"] rule.

🎯 Proposed fix
-  border-bottom: 2px solid currentColor;
+  border-bottom: 2px solid currentcolor;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
border-bottom: 2px solid currentColor;
border-bottom: 2px solid currentcolor;
🧰 Tools
🪛 Stylelint (17.3.0)

[error] 384-384: Expected "currentColor" to be "currentcolor" (value-keyword-case)

(value-keyword-case)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/document_anonymizer/web/static/css/app.css` at line 384, The CSS
declaration "border-bottom: 2px solid currentColor;" uses the mixed-case keyword
"currentColor" which violates the project's stylelint rule value-keyword-case:
["lower"]; update the value to the lowercase keyword "currentcolor" (i.e.,
change currentColor to currentcolor) so the rule passes and the declaration
remains equivalent.

font-size: 0.875rem;
font-size: 0.8125rem;
white-space: pre-wrap;
word-break: break-word;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check the CSS file for lines 404 and 744
head -n 750 src/document_anonymizer/web/static/css/app.css | tail -n +400 | head -n 10

Repository: JuliusScheuerer/document-anonymizer

Length of output: 327


🏁 Script executed:

# Also check line 744 specifically
sed -n '740,750p' src/document_anonymizer/web/static/css/app.css

Repository: JuliusScheuerer/document-anonymizer

Length of output: 329


🌐 Web query:

CSS word-break: break-word deprecated modern replacement overflow-wrap

💡 Result:

word-break: break-word is a legacy/non-standard value and is deprecated. Use overflow-wrap instead:

  • Closest behavioral equivalent to word-break: break-word:

    overflow-wrap: anywhere;
    word-break: normal;

    (MDN notes word-break: break-word has the same effect as this combination.) [1]

  • Common “break only if needed to prevent overflow” replacement:

    overflow-wrap: break-word;

    (Standard property; break-word is like anywhere but with a subtle intrinsic sizing difference.) [2]

  • If you need legacy naming for old codebases, word-wrap is a legacy alias of overflow-wrap in browsers/specs:

    overflow-wrap: break-word;
    word-wrap: break-word; /* alias/fallback */

    [3]

Sources: MDN word-break [1], MDN overflow-wrap [2], W3C CSS Text (alias note for word-wrap) [3].


Replace deprecated word-break: break-word with overflow-wrap.

Lines 404 and 744 use the non-standard word-break: break-word value. Replace with:

-  word-break: break-word;
+  overflow-wrap: anywhere;
+  word-break: normal;

This provides modern CSS compliance and consistent cross-browser behavior.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
word-break: break-word;
overflow-wrap: anywhere;
word-break: normal;
🧰 Tools
🪛 Stylelint (17.3.0)

[error] 404-404: Unexpected deprecated keyword "break-word" for property "word-break" (declaration-property-value-keyword-no-deprecated)

(declaration-property-value-keyword-no-deprecated)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/document_anonymizer/web/static/css/app.css` at line 404, Locate the two
occurrences of the non-standard CSS declaration "word-break: break-word" and
replace each with the modern property "overflow-wrap: anywhere;" (remove the old
declaration); update the same selectors that currently contain "word-break:
break-word" so they now use "overflow-wrap: anywhere" for compliant, consistent
word-wrapping behavior.

…ering (#4)

- Normalize CRLF/CR line endings to LF at server boundary in both
  /detect and /anonymize-form endpoints, preventing entity position
  mismatches when text round-trips through HTML hidden inputs
- Add "Beispieltext laden" link to load sample German PII into textarea
  for quick testing, with warm-palette styling
- Replace HTML entities (&#8805;, &ndash;) with UTF-8 characters in
  tier config to fix double-escaping by Jinja2 autoescaping
- Add debug logging for entity skip reasons in both text and PDF
  reconstruction paths for better diagnostics
- Add tests for line ending normalization and score range validation
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (3)
src/document_anonymizer/web/static/css/app.css (3)

404-404: ⚠️ Potential issue | 🟡 Minor

Replace deprecated word-break: break-word usage.

Line 404 and Line 744 use a deprecated value; switch to overflow-wrap-based wrapping.

🎯 Proposed fix
-  word-break: break-word;
+  overflow-wrap: anywhere;
+  word-break: normal;

Also applies to: 744-744

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/document_anonymizer/web/static/css/app.css` at line 404, Replace the
deprecated CSS property usage by locating the declarations that currently set
"word-break: break-word" (the rules containing that exact string) and change
them to use modern wrapping: remove "word-break: break-word" and add
"overflow-wrap: anywhere;" (and optionally "word-wrap: break-word;" for older
browsers) so text wrapping behavior is preserved.

188-194: ⚠️ Potential issue | 🟡 Minor

Unquote Georgia in font stacks to satisfy font-family-name-quotes.

'Georgia' should be unquoted in these declarations (Line 188, Line 194, Line 705, Line 729).

🎯 Proposed fix
-  font-family: 'Instrument Serif', 'Georgia', serif;
+  font-family: 'Instrument Serif', Georgia, serif;

Also applies to: 705-705, 729-729

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/document_anonymizer/web/static/css/app.css` around lines 188 - 194,
Remove the unnecessary quotes around Georgia in the font-family declarations:
update the font-family values in the shown rule (the block ending just before
.text-lg) and in the .text-lg rule so they read Instrument Serif, Georgia, serif
(and do the same for the other occurrences flagged in the stylesheet). Search
for any other font-family declarations that currently use 'Georgia' and unquote
them to satisfy the font-family-name-quotes rule while preserving the
comma-separated order and fallback stack.

384-384: ⚠️ Potential issue | 🟡 Minor

Use lowercase currentcolor for lint compliance.

Line 384 uses currentColor, but configured keyword-case linting expects lowercase.

🎯 Proposed fix
-  border-bottom: 2px solid currentColor;
+  border-bottom: 2px solid currentcolor;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/document_anonymizer/web/static/css/app.css` at line 384, The CSS property
declaration using "currentColor" should use lowercase to satisfy keyword-case
linting: find the rule containing "border-bottom: 2px solid currentColor" and
change the keyword to "currentcolor" so the declaration reads "border-bottom:
2px solid currentcolor".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/document_anonymizer/web/static/css/app.css`:
- Line 404: Replace the deprecated CSS property usage by locating the
declarations that currently set "word-break: break-word" (the rules containing
that exact string) and change them to use modern wrapping: remove "word-break:
break-word" and add "overflow-wrap: anywhere;" (and optionally "word-wrap:
break-word;" for older browsers) so text wrapping behavior is preserved.
- Around line 188-194: Remove the unnecessary quotes around Georgia in the
font-family declarations: update the font-family values in the shown rule (the
block ending just before .text-lg) and in the .text-lg rule so they read
Instrument Serif, Georgia, serif (and do the same for the other occurrences
flagged in the stylesheet). Search for any other font-family declarations that
currently use 'Georgia' and unquote them to satisfy the font-family-name-quotes
rule while preserving the comma-separated order and fallback stack.
- Line 384: The CSS property declaration using "currentColor" should use
lowercase to satisfy keyword-case linting: find the rule containing
"border-bottom: 2px solid currentColor" and change the keyword to "currentcolor"
so the declaration reads "border-bottom: 2px solid currentcolor".

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 838caf41-2027-44da-b1d7-1c6493d045d8

📥 Commits

Reviewing files that changed from the base of the PR and between 249a820 and ae25626.

📒 Files selected for processing (6)
  • src/document_anonymizer/web/routes.py
  • src/document_anonymizer/web/static/css/app.css
  • src/document_anonymizer/web/static/js/app.js
  • src/document_anonymizer/web/templates/index.html
  • src/document_anonymizer/web/templates/results.html
  • tests/test_web/test_review_helpers.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/document_anonymizer/web/templates/index.html
  • src/document_anonymizer/web/templates/results.html

Address all review findings from the ux-overhaul PR:

- Add conditional Cache-Control: public for static assets, no-store for
  dynamic responses (with status_code==200 guard to prevent cached 404s)
- Fix WCAG contrast: btn-primary amber-500→amber-700, footer/muted text
  warm-400→warm-500
- Extract CSS design tokens: font stacks, RGB channel vars for rgba()
- Remove unused CSS classes and italic font files (~34KB saved)
- Replace inline styles with utility classes across all templates
- Add font preload hints for primary latin subsets
- Add .gitattributes for binary woff2 handling
- Add 5 new tests: static caching, PII no-cache, cache branching unit tests
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/document_anonymizer/security/middleware.py (1)

36-45: ⚠️ Potential issue | 🟠 Major

CSP currently blocks the new Google Fonts setup.

At Line 39 and Line 41, CSP only allows local styles/fonts. With Google Fonts in the UI, these requests will be blocked unless fonts.googleapis.com and fonts.gstatic.com are allowlisted.

Suggested fix
             response.headers["Content-Security-Policy"] = (
                 "default-src 'self'; "
                 "script-src 'self'; "
-                "style-src 'self' 'unsafe-inline'; "
+                "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; "
                 "img-src 'self' data:; "
-                "font-src 'self'; "
+                "font-src 'self' https://fonts.gstatic.com; "
                 "frame-ancestors 'none'; "
                 "base-uri 'self'; "
                 "form-action 'self'"
             )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/document_anonymizer/security/middleware.py` around lines 36 - 45, The CSP
set on response.headers["Content-Security-Policy"] in security.middleware.py
currently restricts styles and fonts to 'self' causing Google Fonts requests to
be blocked; update the CSP string in that block (the response.headers
assignment) to allowlist the Google Fonts hosts by adding
https://fonts.googleapis.com (or fonts.googleapis.com) to style-src and
https://fonts.gstatic.com (or fonts.gstatic.com) to font-src (optionally include
the https: scheme and consider keeping 'unsafe-inline' for styles if needed) so
the UI can load Google Fonts successfully.
♻️ Duplicate comments (2)
src/document_anonymizer/web/static/css/app.css (2)

199-199: ⚠️ Potential issue | 🟡 Minor

Replace deprecated word-break: break-word usage.

Line [199], Line [385], and Line [714] use a deprecated value. Use overflow-wrap and keep word-break at normal where needed.

Proposed fix
-.break-words { word-break: break-word; }
+.break-words {
+  overflow-wrap: anywhere;
+  word-break: normal;
+}
@@
-  word-break: break-word;
+  overflow-wrap: anywhere;
+  word-break: normal;
@@
-  word-break: break-word;
+  overflow-wrap: anywhere;
+  word-break: normal;
#!/bin/bash
set -e
rg -n "word-break:\\s*break-word" src/document_anonymizer/web/static/css/app.css
cat .stylelintrc.json | rg -n "declaration-property-value-keyword-no-deprecated"

Also applies to: 385-385, 714-714

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/document_anonymizer/web/static/css/app.css` at line 199, Replace the
deprecated usage of "word-break: break-word" in the .break-words rule (and the
other two occurrences) by setting overflow-wrap: break-word and keeping
word-break: normal; locate the CSS rules that use "word-break: break-word"
(e.g., the .break-words class) and change them to use overflow-wrap: break-word
plus word-break: normal to preserve behavior without the deprecated value.

365-365: ⚠️ Potential issue | 🟡 Minor

Normalize currentColor casing for lint compliance.

At Line [365], use lowercase currentcolor to satisfy value-keyword-case.

Proposed fix
-  border-bottom: 2px solid currentColor;
+  border-bottom: 2px solid currentcolor;
#!/bin/bash
set -e
rg -n "currentColor|currentcolor" src/document_anonymizer/web/static/css/app.css
cat .stylelintrc.json | rg -n '"value-keyword-case"'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/document_anonymizer/web/static/css/app.css` at line 365, The CSS uses the
keyword with incorrect casing in the declaration `border-bottom: 2px solid
currentColor;` — update the value to lowercase `currentcolor` to satisfy the
`value-keyword-case` rule; locate the `border-bottom` declaration in
src/document_anonymizer/web/static/css/app.css (the line containing
`border-bottom: 2px solid currentColor;`) and replace `currentColor` with
`currentcolor`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/test_web/test_routes.py`:
- Around line 280-285: The CSP test test_csp_header_present currently only
asserts "font-src 'self'" and should also assert that Google Fonts are
allowlisted; update the assertions on the csp string (from
r.headers["Content-Security-Policy"]) to explicitly check for
fonts.googleapis.com and fonts.gstatic.com (or their expected sources) being
present in the font-src directive so missing Google Fonts entries are detected.

---

Outside diff comments:
In `@src/document_anonymizer/security/middleware.py`:
- Around line 36-45: The CSP set on response.headers["Content-Security-Policy"]
in security.middleware.py currently restricts styles and fonts to 'self' causing
Google Fonts requests to be blocked; update the CSP string in that block (the
response.headers assignment) to allowlist the Google Fonts hosts by adding
https://fonts.googleapis.com (or fonts.googleapis.com) to style-src and
https://fonts.gstatic.com (or fonts.gstatic.com) to font-src (optionally include
the https: scheme and consider keeping 'unsafe-inline' for styles if needed) so
the UI can load Google Fonts successfully.

---

Duplicate comments:
In `@src/document_anonymizer/web/static/css/app.css`:
- Line 199: Replace the deprecated usage of "word-break: break-word" in the
.break-words rule (and the other two occurrences) by setting overflow-wrap:
break-word and keeping word-break: normal; locate the CSS rules that use
"word-break: break-word" (e.g., the .break-words class) and change them to use
overflow-wrap: break-word plus word-break: normal to preserve behavior without
the deprecated value.
- Line 365: The CSS uses the keyword with incorrect casing in the declaration
`border-bottom: 2px solid currentColor;` — update the value to lowercase
`currentcolor` to satisfy the `value-keyword-case` rule; locate the
`border-bottom` declaration in src/document_anonymizer/web/static/css/app.css
(the line containing `border-bottom: 2px solid currentColor;`) and replace
`currentColor` with `currentcolor`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78b5bc4b-1047-417b-9440-f001f5478369

📥 Commits

Reviewing files that changed from the base of the PR and between ae25626 and 1e26bb4.

📒 Files selected for processing (9)
  • .gitattributes
  • src/document_anonymizer/security/middleware.py
  • src/document_anonymizer/web/static/css/app.css
  • src/document_anonymizer/web/templates/anonymized.html
  • src/document_anonymizer/web/templates/base.html
  • src/document_anonymizer/web/templates/index.html
  • src/document_anonymizer/web/templates/results.html
  • tests/test_security/test_middleware.py
  • tests/test_web/test_routes.py
✅ Files skipped from review due to trivial changes (1)
  • .gitattributes
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/document_anonymizer/web/templates/base.html
  • src/document_anonymizer/web/templates/anonymized.html

Comment on lines 280 to +285
def test_csp_header_present(self) -> None:
r = client.get("/")
csp = r.headers["Content-Security-Policy"]
assert "default-src 'self'" in csp
assert "frame-ancestors 'none'" in csp
assert "font-src 'self'" in csp
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Strengthen CSP test to verify Google Fonts allowlist explicitly.

At Line 285, checking only "font-src 'self'" is insufficient; it won’t catch missing Google Fonts domains in CSP.

Suggested test hardening
         csp = r.headers["Content-Security-Policy"]
         assert "default-src 'self'" in csp
         assert "frame-ancestors 'none'" in csp
         assert "font-src 'self'" in csp
+        assert "fonts.googleapis.com" in csp
+        assert "fonts.gstatic.com" in csp
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_web/test_routes.py` around lines 280 - 285, The CSP test
test_csp_header_present currently only asserts "font-src 'self'" and should also
assert that Google Fonts are allowlisted; update the assertions on the csp
string (from r.headers["Content-Security-Policy"]) to explicitly check for
fonts.googleapis.com and fonts.gstatic.com (or their expected sources) being
present in the font-src directive so missing Google Fonts entries are detected.

@JuliusScheuerer JuliusScheuerer merged commit 2fc8689 into main Mar 5, 2026
7 checks passed
@JuliusScheuerer JuliusScheuerer deleted the ux-overhaul branch March 5, 2026 11:10
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.

1 participant