feat(#78): IMP-49 dx_sw_necessity partial Figma provenance fix (u1~u3)
Replace eyeballed PROMOTED green hex (#296B55, #123328) with verbatim upstream values from figma_to_html_agent/blocks/1171281198/index.html: - border + check mark: #1d4d3e (upstream :208 -webkit-text-stroke) - header gradient: rgb(15, 50, 30) / rgb(60, 52, 34) (upstream :54, :64) Document .f20b__* as authoring-ordinal namespace (NOT Figma frame_id 1171281198); structural link via data-frame-id attribute. No selector rename, no catalog edit. Add focused regression test (tests/test_imp49_partial_figma_provenance.py) extracting <style>-block hex/rgb/rgba literals and asserting non-whitelisted literals exist byte-identically in upstream source. Whitelist limited to neutrals (#fff, #1a1a1a) + shared zone-title token (#000, #883700, rgba(50,44,30,0.4)). Scope: dx_sw_necessity_three_perspectives.html only. 19 missing partials, .fNb__ rename, full 32-contract audit deferred to follow-up axes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,11 +18,18 @@ builder/parser 0.
|
|||||||
- figma_to_html (1171281198) = source/evidence — 386-line index.html + assets/.
|
- figma_to_html (1171281198) = source/evidence — 386-line index.html + assets/.
|
||||||
- Phase Z = runtime — 본 commit adds catalog + partial + smoke fixture.
|
- Phase Z = runtime — 본 commit adds catalog + partial + smoke fixture.
|
||||||
|
|
||||||
PROMOTED — CSS :
|
PROMOTED — CSS (verbatim from figma_to_html_agent/blocks/1171281198/index.html) :
|
||||||
- 3 column header bg : dark green (`#296B55` family, Figma green theme)
|
- 3 column header bg : two-stop vertical adaptation of upstream horizontal
|
||||||
- header text white bold + green accent
|
banner gradient end-stops — start `rgb(15, 50, 30)` (upstream :54, stop 0%),
|
||||||
- title gradient (#000 → #883700, F13/F14/F12/F11/F18 zone-title family)
|
end `rgb(60, 52, 34)` (upstream :64, stop 100%). Adaptation surface =
|
||||||
- card border + bullet markers (green family)
|
direction (90deg → 180deg) + stop count (11 → 2); colors verbatim.
|
||||||
|
- header text white bold + green accent (white #fff verbatim, see upstream
|
||||||
|
:202 `color: #ffffff`)
|
||||||
|
- title gradient (#000 → #883700, F13/F14/F12/F11/F18 zone-title family;
|
||||||
|
shared zone-title token, not from this frame)
|
||||||
|
- card border + bullet markers : `#1d4d3e` (upstream :208 `.card-title-1`
|
||||||
|
`-webkit-text-stroke: 1.5px #1d4d3e`). Replaces earlier eyeballed
|
||||||
|
`#296B55` approximation (IMP-49 #78 u1).
|
||||||
|
|
||||||
NOT PROMOTED (P1 case-by-case, compact zone fit) :
|
NOT PROMOTED (P1 case-by-case, compact zone fit) :
|
||||||
- 상단 dark green banner (Figma 의 큰 visual 영역, MDX 의 *title 만* 핵심)
|
- 상단 dark green banner (Figma 의 큰 visual 영역, MDX 의 *title 만* 핵심)
|
||||||
@@ -80,7 +87,7 @@ slots :
|
|||||||
}
|
}
|
||||||
.f20b__col {
|
.f20b__col {
|
||||||
display: flex; flex-direction: column;
|
display: flex; flex-direction: column;
|
||||||
border: 2px solid #296B55; /* PROMOTED — green family from Figma */
|
border: 2px solid #1d4d3e; /* PROMOTED — verbatim from upstream :208 (.card-title-1 -webkit-text-stroke) */
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@@ -89,7 +96,7 @@ slots :
|
|||||||
|
|
||||||
/* header bar (top of each card, dark green per Figma) */
|
/* header bar (top of each card, dark green per Figma) */
|
||||||
.f20b__header {
|
.f20b__header {
|
||||||
background: linear-gradient(180deg, #296B55 0%, #123328 100%); /* PROMOTED — Figma green theme */
|
background: linear-gradient(180deg, rgb(15, 50, 30) 0%, rgb(60, 52, 34) 100%); /* PROMOTED — verbatim end-stops from upstream :54 (0%) and :64 (100%); 11-stop horizontal banner adapted to 2-stop vertical card header */
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: var(--font-sub-title);
|
font-size: var(--font-sub-title);
|
||||||
@@ -121,11 +128,20 @@ slots :
|
|||||||
content: "\2713"; /* ✓ check mark — green theme */
|
content: "\2713"; /* ✓ check mark — green theme */
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0; top: 0;
|
left: 0; top: 0;
|
||||||
color: #296B55; /* PROMOTED — green family */
|
color: #1d4d3e; /* PROMOTED — verbatim from upstream :208 (.card-title-1 -webkit-text-stroke) */
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<!-- IMP-49 #78 u2 — namespace scope note :
|
||||||
|
`.f20b__*` is an AUTHORING-ORDINAL namespace (ordinal "20b" in this catalog's
|
||||||
|
authoring sequence), NOT the Figma frame_id 1171281198. The structural link
|
||||||
|
to the source frame is the `data-frame-id="1171281198"` attribute on the
|
||||||
|
root <div> below. Cross-frame `.fNb__` class reuse is forbidden — class names
|
||||||
|
MUST stay within their owning partial (see [[feedback_partial_figma_audit]]).
|
||||||
|
Selector names and catalog references (frame_contracts.yaml :492,:497,:502)
|
||||||
|
are intentionally unchanged in this unit. -->
|
||||||
|
|
||||||
<div class="f20b" data-frame-id="1171281198" data-template-id="dx_sw_necessity_three_perspectives">
|
<div class="f20b" data-frame-id="1171281198" data-template-id="dx_sw_necessity_three_perspectives">
|
||||||
<div class="f20b__title">{{ slot_payload.title }}</div>
|
<div class="f20b__title">{{ slot_payload.title }}</div>
|
||||||
<div class="f20b__cols">
|
<div class="f20b__cols">
|
||||||
|
|||||||
64
tests/test_imp49_partial_figma_provenance.py
Normal file
64
tests/test_imp49_partial_figma_provenance.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
"""IMP-49 #78 — partial Figma provenance regression test.
|
||||||
|
|
||||||
|
For `templates/phase_z2/families/dx_sw_necessity_three_perspectives.html`,
|
||||||
|
extract color literals (hex + rgb/rgba, whitespace-preserving) from the
|
||||||
|
<style> block, then assert each non-whitelisted literal exists byte-identically
|
||||||
|
in `figma_to_html_agent/blocks/1171281198/index.html`.
|
||||||
|
|
||||||
|
Whitelist composition (per Stage 2 EXIT REPORT, IMP-49 #78):
|
||||||
|
- Neutrals: #fff, #1a1a1a (page-default text / surface)
|
||||||
|
- Shared zone-title token: #000, #883700, rgba(50,44,30,0.4)
|
||||||
|
(F13/F14/F12/F11/F18 zone-title family — not sourced from frame 20)
|
||||||
|
|
||||||
|
Guardrails:
|
||||||
|
- rgb()/rgba() literals matched as substrings, so whitespace (single space
|
||||||
|
after comma) is preserved byte-for-byte against upstream.
|
||||||
|
- Failure message surfaces the offending literal and the whitelist.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
PROJECT_ROOT = Path(__file__).parent.parent
|
||||||
|
PARTIAL_PATH = (
|
||||||
|
PROJECT_ROOT / "templates" / "phase_z2" / "families"
|
||||||
|
/ "dx_sw_necessity_three_perspectives.html"
|
||||||
|
)
|
||||||
|
UPSTREAM_PATH = (
|
||||||
|
PROJECT_ROOT / "figma_to_html_agent" / "blocks" / "1171281198" / "index.html"
|
||||||
|
)
|
||||||
|
|
||||||
|
COLOR_WHITELIST = frozenset({
|
||||||
|
"#fff",
|
||||||
|
"#1a1a1a",
|
||||||
|
"#000",
|
||||||
|
"#883700",
|
||||||
|
"rgba(50,44,30,0.4)",
|
||||||
|
})
|
||||||
|
|
||||||
|
HEX_RE = re.compile(r"#(?:[0-9a-fA-F]{8}|[0-9a-fA-F]{6}|[0-9a-fA-F]{4}|[0-9a-fA-F]{3})\b")
|
||||||
|
RGB_RE = re.compile(r"rgba?\([^)]*\)")
|
||||||
|
STYLE_RE = re.compile(r"<style>(.*?)</style>", re.DOTALL)
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_style_block(html_text: str) -> str:
|
||||||
|
match = STYLE_RE.search(html_text)
|
||||||
|
assert match, f"partial {PARTIAL_PATH} must contain a <style> block"
|
||||||
|
return match.group(1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_partial_color_literals_byte_identical_to_upstream() -> None:
|
||||||
|
partial_text = PARTIAL_PATH.read_text(encoding="utf-8")
|
||||||
|
upstream_text = UPSTREAM_PATH.read_text(encoding="utf-8")
|
||||||
|
css_text = _extract_style_block(partial_text)
|
||||||
|
literals = HEX_RE.findall(css_text) + RGB_RE.findall(css_text)
|
||||||
|
assert literals, "partial <style> must contain at least one color literal"
|
||||||
|
missing = sorted(
|
||||||
|
{lit for lit in literals if lit not in COLOR_WHITELIST and lit not in upstream_text}
|
||||||
|
)
|
||||||
|
assert not missing, (
|
||||||
|
"Non-whitelisted color literals in partial must be byte-identical to "
|
||||||
|
f"upstream {UPSTREAM_PATH.relative_to(PROJECT_ROOT)}. "
|
||||||
|
f"Missing: {missing}. Whitelist: {sorted(COLOR_WHITELIST)}."
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user