@ptengine/lp-editor-core 1.0.0-alpha.1 → 1.0.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,15 +43,39 @@ const core = createCore({ DOMParser: new JSDOM('').window.DOMParser });
43
43
  const doc = core.parse(sourceHtml);
44
44
  core.normalize(doc); // 唯一分配元素 id 的地方
45
45
 
46
- const { ok, issues } = core.validate(doc, { profile: core.KIND_PROFILES.page });
46
+ const { ok, issues } = core.validate(doc); // 可选开关都不传 = 最宽松
47
47
 
48
- const result = core.apply(doc, ops, { profile: core.KIND_PROFILES.page });
48
+ const result = core.apply(doc, ops);
49
49
  if (result.ok) {
50
50
  const html = core.serialize(doc); // 规范序列化,两侧同字节
51
51
  const undo = result.inverse; // 逆操作,喂给撤销栈
52
52
  }
53
53
  ```
54
54
 
55
+ **core 不感知宿主**:它只认一份 v4 文档、一套三层树、一套 EditOp,没有「落地页 / 弹窗」
56
+ 这类档位入参。宿主差异(运行时契约标记、样式作用域与单位换算、可用能力开关)由调用方在
57
+ 进出边界上处理。少数可选规则以**扁平开关**的形式提供:
58
+
59
+ ```ts
60
+ core.validate(doc, {
61
+ allowVhUnits: false, // 文档被装在比视口小的框里时:vh / h-screen 按整个视口算,不是那个框
62
+ singleSection: true, // 限定整份文档只有一个 section
63
+ canCompileClass // 注入「这个 class 在当前 @theme 下能不能编译」的判定
64
+ });
65
+ ```
66
+
67
+ 持有 HTML **片段**(而不是完整文档)的调用方,在读出与写回的那一刻各调一次包裹 / 剥离:
68
+
69
+ ```ts
70
+ const doc = core.wrap(fragmentHtml); // 片段 → 合规的三层树文档,编辑器内部没有「片段」这回事
71
+ // …编辑…
72
+ const html = core.serialize(core.unwrap(doc)); // 无损剥回原来的形态
73
+ ```
74
+
75
+ 包裹会把片段的根元素**认领**成文档的 `<body>`(不改名、不另起一层),并合成一个 `section`
76
+ + 一个 `container`(都带 `data-pt-wrap` 标记)。往返无损由 golden 锁住。遇到不合约定的输入
77
+ **一律抛错**,不自行修复或改名。
78
+
55
79
  ### 主要导出
56
80
 
57
81
  | 导出 | 作用 |
@@ -64,7 +88,8 @@ if (result.ok) {
64
88
  | `compile.{incremental,fresh,warm}` | Tailwind 编译。`fresh` 的输出只取决于内容 |
65
89
  | `collectClasses` | 收集文档用到的 class |
66
90
  | `guards` | AI 护栏:id 命中、片段 id 齐全、禁脚本与事件属性 |
67
- | `rules` / `KIND_PROFILES` | 规则数据表与两种文档类型的能力档 |
91
+ | `wrap` / `unwrap` | 片段型来源的规范化包裹与无损剥离 |
92
+ | `rules` | 规则数据表(层级、变体允许集、保留字面量、结构开关…) |
68
93
  | `VERSION` / `COMPILER_VERSION` | 包版本 / **编译产物版本**(两者独立,见下) |
69
94
 
70
95
  ### 两个版本号
@@ -0,0 +1,184 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ja">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="lpx-format" content="4">
7
+ <style type="text/tailwindcss" data-pt-id="theme">
8
+ @theme {
9
+ --color-brand: #8b7355;
10
+ --color-ink: #3a3330;
11
+ --color-muted: #8a817c;
12
+ --color-surface: #ffffff;
13
+ --color-tint: #f7f3ee;
14
+ --color-line: #e5ded6;
15
+ --font-sans: "Noto Sans JP", sans-serif;
16
+ --text-display-1: clamp(1.75rem, 5vw, 2.75rem);
17
+ --text-display-2: clamp(1.375rem, 3.6vw, 2rem);
18
+ --text-lead: clamp(0.9375rem, 2.2vw, 1.125rem);
19
+ --spacing-section-y: clamp(2.5rem, 6vw, 5rem);
20
+ }
21
+ </style>
22
+ </head>
23
+ <body data-pt-id="root" class="flex flex-col bg-surface text-ink font-sans">
24
+
25
+ <section data-pt-id="s1" data-pt-name="首屏" class="py-section-y px-4">
26
+ <div data-pt-id="c1" class="mx-auto flex max-w-[750px] flex-col items-center gap-4">
27
+ <p data-pt-type="text" data-pt-id="t1" class="text-sm tracking-widest text-brand">UV AQ SERUM 50</p>
28
+ <h1 data-pt-type="text" data-pt-id="t2" class="text-center text-display-1 font-bold leading-tight">日中の乾燥から守る、<br>美容液のような日やけ止め</h1>
29
+ <picture data-pt-type="media" data-pt-id="m1">
30
+ <source media="(min-width:768px)" srcset="hero-pc.jpg">
31
+ <img src="hero-sp.jpg" alt="UV AQ セラム 50 本体" class="w-full aspect-[4/3] object-cover">
32
+ </picture>
33
+ <p data-pt-type="text" data-pt-id="t3" class="text-center text-lead text-muted">SPF50+ / PA++++ 石けんで落とせる</p>
34
+ <a data-pt-type="button" data-pt-id="b1" data-pt-track="cta_hero" href="#order" class="flex min-h-12 w-full max-w-[420px] items-center justify-center rounded bg-brand text-base font-bold text-surface">ご購入はこちら</a>
35
+ </div>
36
+ </section>
37
+
38
+ <section data-pt-id="s2" data-pt-name="悩み" class="bg-tint py-section-y px-4">
39
+ <div data-pt-id="c2" class="mx-auto flex max-w-[750px] flex-col gap-5">
40
+ <h2 data-pt-type="text" data-pt-id="t4" class="text-center text-display-2 font-bold">こんなお悩みはありませんか</h2>
41
+ <div data-pt-id="c3" class="grid grid-cols-1 gap-3 md:grid-cols-2">
42
+ <p data-pt-type="text" data-pt-id="t5" class="rounded bg-surface p-4 text-sm">日やけ止めをつけると乾燥する</p>
43
+ <p data-pt-type="text" data-pt-id="t6" class="rounded bg-surface p-4 text-sm">白浮きしてトーンが不自然になる</p>
44
+ <p data-pt-type="text" data-pt-id="t7" class="rounded bg-surface p-4 text-sm">きしみが気になって塗り直せない</p>
45
+ <p data-pt-type="text" data-pt-id="t8" class="rounded bg-surface p-4 text-sm">敏感肌でも使えるか不安</p>
46
+ </div>
47
+ </div>
48
+ </section>
49
+
50
+ <section data-pt-id="s3" data-pt-name="提案" class="py-section-y px-4">
51
+ <div data-pt-id="c4" class="mx-auto flex max-w-[750px] flex-col items-center gap-4">
52
+ <h2 data-pt-type="text" data-pt-id="t9" class="text-center text-display-2 font-bold">その答えが、美容液という発想</h2>
53
+ <img data-pt-type="media" data-pt-id="m2" data-pt-placeholder data-pt-image-prompt="テクスチャーを手の甲に伸ばしたクローズアップ。透明感のある水っぽい質感" src="" class="w-full aspect-[16/10] object-cover">
54
+ <p data-pt-type="text" data-pt-id="t10" class="text-lead leading-relaxed">うるおいを与えながら、日中の紫外線から肌を守ります。</p>
55
+ <a data-pt-type="button" data-pt-id="b2" data-pt-track="cta_mid1" href="#order" class="flex min-h-12 w-full max-w-[420px] items-center justify-center rounded bg-brand text-base font-bold text-surface">ご購入はこちら</a>
56
+ </div>
57
+ </section>
58
+
59
+ <section data-pt-id="s4" data-pt-name="お客様の声" class="bg-tint py-section-y px-4">
60
+ <div data-pt-id="c5" class="mx-auto flex max-w-[750px] flex-col gap-4">
61
+ <h2 data-pt-type="text" data-pt-id="t11" class="text-center text-display-2 font-bold">使った方の声</h2>
62
+ <div data-pt-id="c6" class="flex flex-col gap-3">
63
+ <div data-pt-id="c7" class="flex gap-3 rounded bg-surface p-4">
64
+ <img data-pt-type="media" data-pt-id="m3" src="voice1.jpg" alt="30代 女性" class="w-14 aspect-square rounded-full object-cover">
65
+ <p data-pt-type="text" data-pt-id="t12" class="flex-1 text-sm leading-relaxed">つっぱらないので朝のメイク前でも気になりません。</p>
66
+ </div>
67
+ <div data-pt-id="c8" class="flex gap-3 rounded bg-surface p-4">
68
+ <img data-pt-type="media" data-pt-id="m4" src="voice2.jpg" alt="40代 女性" class="w-14 aspect-square rounded-full object-cover">
69
+ <p data-pt-type="text" data-pt-id="t13" class="flex-1 text-sm leading-relaxed">石けんで落とせるのが毎日うれしいです。</p>
70
+ </div>
71
+ <div data-pt-id="c9" class="flex gap-3 rounded bg-surface p-4">
72
+ <img data-pt-type="media" data-pt-id="m5" data-pt-placeholder data-pt-image-prompt="50代女性のポートレート、ナチュラルメイク、正方形" src="" class="w-14 aspect-square rounded-full object-cover">
73
+ <p data-pt-type="text" data-pt-id="t14" class="flex-1 text-sm leading-relaxed">敏感肌ですが刺激を感じませんでした。</p>
74
+ </div>
75
+ </div>
76
+ </div>
77
+ </section>
78
+
79
+ <section data-pt-id="s5" data-pt-name="4つの特長" class="py-section-y px-4">
80
+ <div data-pt-id="c10" class="mx-auto flex max-w-[750px] flex-col gap-5">
81
+ <h2 data-pt-type="text" data-pt-id="t15" class="text-center text-display-2 font-bold">4つの特長</h2>
82
+ <div data-pt-id="c11" class="grid grid-cols-1 gap-4 md:grid-cols-2">
83
+ <div data-pt-id="c12" class="flex flex-col gap-2">
84
+ <img data-pt-type="media" data-pt-id="m6" src="point1.jpg" alt="うるおい" class="w-full aspect-[3/2] object-cover">
85
+ <h3 data-pt-type="text" data-pt-id="t16" class="text-base font-bold">01 美容液のようなうるおい</h3>
86
+ <p data-pt-type="text" data-pt-id="t17" class="text-sm text-muted">保湿成分を高配合しました。</p>
87
+ </div>
88
+ <div data-pt-id="c13" class="flex flex-col gap-2">
89
+ <img data-pt-type="media" data-pt-id="m7" src="point2.jpg" alt="白浮きしない" class="w-full aspect-[3/2] object-cover">
90
+ <h3 data-pt-type="text" data-pt-id="t18" class="text-base font-bold">02 白浮きしない仕上がり</h3>
91
+ <p data-pt-type="text" data-pt-id="t19" class="text-sm text-muted">素肌のような透明感。</p>
92
+ </div>
93
+ <div data-pt-id="c14" class="flex flex-col gap-2">
94
+ <img data-pt-type="media" data-pt-id="m8" data-pt-placeholder data-pt-image-prompt="石けんで洗い流すシーン、泡と水、横長" src="" class="w-full aspect-[3/2] object-cover">
95
+ <h3 data-pt-type="text" data-pt-id="t20" class="text-base font-bold">03 石けんで落とせる</h3>
96
+ <p data-pt-type="text" data-pt-id="t21" class="text-sm text-muted">専用クレンジングは不要です。</p>
97
+ </div>
98
+ <div data-pt-id="c15" class="flex flex-col gap-2">
99
+ <img data-pt-type="media" data-pt-id="m9" src="point4.jpg" alt="低刺激設計" class="w-full aspect-[3/2] object-cover">
100
+ <h3 data-pt-type="text" data-pt-id="t22" class="text-base font-bold">04 低刺激設計</h3>
101
+ <p data-pt-type="text" data-pt-id="t23" class="text-sm text-muted">パッチテスト済み。</p>
102
+ </div>
103
+ </div>
104
+ <a data-pt-type="button" data-pt-id="b3" data-pt-track="cta_mid2" href="#order" class="mx-auto flex min-h-12 w-full max-w-[420px] items-center justify-center rounded bg-brand text-base font-bold text-surface">ご購入はこちら</a>
105
+ </div>
106
+ </section>
107
+
108
+ <section data-pt-id="s6" data-pt-name="成分" class="bg-tint py-section-y px-4">
109
+ <div data-pt-id="c16" class="mx-auto flex max-w-[750px] flex-col gap-4">
110
+ <h2 data-pt-type="text" data-pt-id="t24" class="text-center text-display-2 font-bold">配合成分</h2>
111
+ <img data-pt-type="media" data-pt-id="m10" src="ingredients.jpg" alt="成分イメージ" class="w-full aspect-[16/9] object-cover">
112
+ <p data-pt-type="text" data-pt-id="t25" class="text-sm leading-relaxed text-muted">セラミド、ヒアルロン酸、植物由来エキスを配合しています。</p>
113
+ </div>
114
+ </section>
115
+
116
+ <section data-pt-id="s7" data-pt-name="使用感" class="py-section-y px-4">
117
+ <div data-pt-id="c17" class="mx-auto flex max-w-[750px] flex-col gap-4">
118
+ <h2 data-pt-type="text" data-pt-id="t26" class="text-center text-display-2 font-bold">テクスチャー</h2>
119
+ <div data-pt-id="c18" class="grid grid-cols-3 gap-2">
120
+ <img data-pt-type="media" data-pt-id="m11" src="tex1.jpg" alt="テクスチャー1" class="w-full aspect-square object-cover">
121
+ <img data-pt-type="media" data-pt-id="m12" src="tex2.jpg" alt="テクスチャー2" class="w-full aspect-square object-cover">
122
+ <img data-pt-type="media" data-pt-id="m13" src="tex3.jpg" alt="テクスチャー3" class="w-full aspect-square object-cover">
123
+ </div>
124
+ </div>
125
+ </section>
126
+
127
+ <section data-pt-id="s8" data-pt-name="安全性" class="bg-tint py-section-y px-4">
128
+ <div data-pt-id="c19" class="mx-auto flex max-w-[750px] flex-col gap-4">
129
+ <h2 data-pt-type="text" data-pt-id="t27" class="text-center text-display-2 font-bold">低刺激設計について</h2>
130
+ <p data-pt-type="text" data-pt-id="t28" class="text-sm leading-relaxed">アルコール・鉱物油・合成香料は使用していません。</p>
131
+ <a data-pt-type="button" data-pt-id="b4" data-pt-track="cta_mid3" href="#order" class="mx-auto flex min-h-12 w-full max-w-[420px] items-center justify-center rounded bg-brand text-base font-bold text-surface">ご購入はこちら</a>
132
+ </div>
133
+ </section>
134
+
135
+ <section data-pt-id="s9" data-pt-name="FAQ" class="py-section-y px-4">
136
+ <div data-pt-id="c20" class="mx-auto flex max-w-[750px] flex-col gap-4">
137
+ <h2 data-pt-type="text" data-pt-id="t29" class="text-center text-display-2 font-bold">よくあるご質問</h2>
138
+ <div data-pt-id="k1" data-pt-component="faq" data-pt-component-settings='{"items":[{"q":"敏感肌でも使えますか","a":"パッチテスト済みですが、心配な方は twarzy でお試しください。"},{"q":"石けんだけで落ちますか","a":"はい、通常の洗顔料で落とせます。"},{"q":"化粧下地として使えますか","a":"はい、スキンケアの最後にお使いください。"},{"q":"1本でどのくらい持ちますか","a":"顔全体で約2か月分です。"},{"q":"子どもにも使えますか","a":"3歳以上のお子さまにお使いいただけます。"}]}'></div>
139
+ </div>
140
+ </section>
141
+
142
+ <section data-pt-id="s10" data-pt-name="配送と支払い" class="bg-tint py-section-y px-4">
143
+ <div data-pt-id="c21" class="mx-auto flex max-w-[750px] flex-col gap-4">
144
+ <h2 data-pt-type="text" data-pt-id="t30" class="text-center text-display-2 font-bold">配送・お支払いについて</h2>
145
+ <table data-pt-id="tb1" data-pt-opaque="text" class="w-full border border-line text-sm">
146
+ <thead>
147
+ <tr>
148
+ <th data-pt-id="h1" class="border border-line bg-surface p-2 text-left">お支払い方法</th>
149
+ <th data-pt-id="h2" class="border border-line bg-surface p-2 text-left">手数料</th>
150
+ <th data-pt-id="h3" class="border border-line bg-surface p-2 text-left">お届け日数</th>
151
+ </tr>
152
+ </thead>
153
+ <tbody>
154
+ <tr>
155
+ <td data-pt-id="d1" class="border border-line p-2">クレジットカード</td>
156
+ <td data-pt-id="d2" class="border border-line p-2">無料</td>
157
+ <td data-pt-id="d3" class="border border-line p-2">2〜4日</td>
158
+ </tr>
159
+ <tr>
160
+ <td data-pt-id="d4" class="border border-line p-2">代金引換</td>
161
+ <td data-pt-id="d5" class="border border-line p-2">330円</td>
162
+ <td data-pt-id="d6" class="border border-line p-2">2〜4日</td>
163
+ </tr>
164
+ <tr>
165
+ <td data-pt-id="d7" class="border border-line p-2">コンビニ後払い</td>
166
+ <td data-pt-id="d8" class="border border-line p-2">209円</td>
167
+ <td data-pt-id="d9" class="border border-line p-2">3〜5日</td>
168
+ </tr>
169
+ </tbody>
170
+ </table>
171
+ </div>
172
+ </section>
173
+
174
+ <section data-pt-id="s11" data-pt-name="申込" class="py-section-y px-4">
175
+ <div data-pt-id="c22" class="mx-auto flex max-w-[750px] flex-col items-center gap-4">
176
+ <h2 data-pt-type="text" data-pt-id="t31" class="text-center text-display-2 font-bold">ご注文はこちらから</h2>
177
+ <p data-pt-type="text" data-pt-id="t32" class="text-center text-lead">初回限定 通常価格より20%OFF</p>
178
+ <a data-pt-type="button" data-pt-id="b5" data-pt-track="cta_footer" href="#order" class="flex min-h-14 w-full max-w-[420px] items-center justify-center rounded bg-brand text-lg font-bold text-surface">ご購入はこちら</a>
179
+ <p data-pt-type="text" data-pt-id="t33" data-pt-hide="mobile" class="text-center text-xs text-muted">※ 表示価格はすべて税込です。</p>
180
+ </div>
181
+ </section>
182
+
183
+ </body>
184
+ </html>
@@ -0,0 +1,188 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ja">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta name="lpx-format" content="4">
7
+ <style type="text/tailwindcss" data-pt-id="theme">
8
+ @theme {
9
+ --color-brand: #1a1a1a;
10
+ --color-ink: #2f2418;
11
+ --color-muted: #6b6b6b;
12
+ --color-surface: #ffffff;
13
+ --color-tint: #f5f1ea;
14
+ --color-line: #ddd4c7;
15
+ --color-accent: #9b5f3a;
16
+ --font-sans: "Inter", "Noto Sans JP", sans-serif;
17
+ --text-display-1: clamp(2rem, 4.5vw, 3.5rem);
18
+ --text-display-2: clamp(1.5rem, 3vw, 2.25rem);
19
+ --spacing-section-y: clamp(3rem, 6vw, 6rem);
20
+ --radius-card: 0.75rem;
21
+ }
22
+ </style>
23
+ </head>
24
+ <body data-pt-id="root" class="flex flex-col bg-surface text-ink font-sans">
25
+
26
+ <section data-pt-id="s1" data-pt-name="首屏" class="py-section-y px-4 md:px-0">
27
+ <div data-pt-id="c1" class="mx-auto flex max-w-[1200px] flex-col items-center gap-6 md:flex-row">
28
+ <picture data-pt-type="media" data-pt-id="m1" class="w-full md:w-1/2">
29
+ <source media="(min-width:768px)" srcset="hero-wide.jpg">
30
+ <img src="hero-square.jpg" alt="Square Toe Quiet Luxury Loafer" class="w-full aspect-square object-cover md:aspect-[4/3]">
31
+ </picture>
32
+ <div data-pt-id="c2" class="flex w-full flex-col items-center gap-4 md:w-1/2 md:items-start">
33
+ <p data-pt-type="text" data-pt-id="t1" class="text-sm tracking-[0.2em] text-accent">ICON LOAFER</p>
34
+ <h1 data-pt-type="text" data-pt-id="t2" class="text-center text-display-1 font-light leading-tight md:text-left">Square Toe<br>Quiet Luxury Loafer</h1>
35
+ <p data-pt-type="text" data-pt-id="t3" class="text-center text-base text-muted md:text-left">足が喜ぶ超軽量シューズ。甲高幅広・外反母趾の方にも。</p>
36
+ <a data-pt-type="button" data-pt-id="b1" data-pt-track="cta_hero" href="#shop" class="flex min-h-12 w-full items-center justify-center rounded-card bg-brand px-8 text-base font-bold text-surface md:w-auto">この商品をみる</a>
37
+ </div>
38
+ </div>
39
+ </section>
40
+
41
+ <section data-pt-id="s2" data-pt-name="卖点网格" class="bg-tint py-section-y px-4">
42
+ <div data-pt-id="c3" class="mx-auto flex max-w-[1200px] flex-col gap-6">
43
+ <h2 data-pt-type="text" data-pt-id="t4" class="text-center text-display-2 font-light">選ばれている3つの理由</h2>
44
+ <div data-pt-id="c4" class="grid grid-cols-1 gap-6 md:grid-cols-3">
45
+ <div data-pt-id="c5" class="flex flex-col items-center gap-3">
46
+ <img data-pt-type="media" data-pt-id="m2" src="reason-wash.jpg" alt="洗濯機で洗える" class="w-full aspect-[4/3] rounded-card object-cover">
47
+ <h3 data-pt-type="text" data-pt-id="t5" class="text-lg font-bold">洗濯機で洗えて お手入れ簡単</h3>
48
+ <p data-pt-type="text" data-pt-id="t6" class="text-center text-sm text-muted">ネットに入れてそのまま洗えます。</p>
49
+ </div>
50
+ <div data-pt-id="c6" class="flex flex-col items-center gap-3">
51
+ <img data-pt-type="media" data-pt-id="m3" src="reason-water.jpg" alt="撥水加工" class="w-full aspect-[4/3] rounded-card object-cover">
52
+ <h3 data-pt-type="text" data-pt-id="t7" class="text-lg font-bold">突然の雨でも安心 撥水加工</h3>
53
+ <p data-pt-type="text" data-pt-id="t8" class="text-center text-sm text-muted">水をはじく特殊繊維を使用。</p>
54
+ </div>
55
+ <div data-pt-id="c7" class="flex flex-col items-center gap-3">
56
+ <img data-pt-type="media" data-pt-id="m4" data-pt-placeholder data-pt-image-prompt="足元がすっきり見えるスクエアトゥのクローズアップ、横長" src="" class="w-full aspect-[4/3] rounded-card object-cover">
57
+ <h3 data-pt-type="text" data-pt-id="t9" class="text-lg font-bold">洗練されたデザインで 足元すっきり見え</h3>
58
+ <p data-pt-type="text" data-pt-id="t10" class="text-center text-sm text-muted">スクエアトゥが脚を長く見せます。</p>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ </section>
63
+
64
+ <section data-pt-id="s3" data-pt-name="配色一览" class="py-section-y px-4">
65
+ <div data-pt-id="c8" class="mx-auto flex max-w-[1200px] flex-col gap-5">
66
+ <h2 data-pt-type="text" data-pt-id="t11" class="text-center text-display-2 font-light">全6色展開</h2>
67
+ <div data-pt-id="c9" data-pt-layout-mobile="scroll" class="grid grid-cols-2 gap-4 md:grid-cols-6">
68
+ <div data-pt-id="c10" class="flex flex-col items-center gap-2">
69
+ <img data-pt-type="media" data-pt-id="m5" src="color-camel.jpg" alt="Camel" class="w-full aspect-square rounded-card object-cover">
70
+ <p data-pt-type="text" data-pt-id="t12" class="text-xs text-muted">Camel</p>
71
+ </div>
72
+ <div data-pt-id="c11" class="flex flex-col items-center gap-2">
73
+ <img data-pt-type="media" data-pt-id="m6" src="color-black.jpg" alt="Black" class="w-full aspect-square rounded-card object-cover">
74
+ <p data-pt-type="text" data-pt-id="t13" class="text-xs text-muted">Black</p>
75
+ </div>
76
+ <div data-pt-id="c12" class="flex flex-col items-center gap-2">
77
+ <img data-pt-type="media" data-pt-id="m7" src="color-navy.jpg" alt="Navy" class="w-full aspect-square rounded-card object-cover">
78
+ <p data-pt-type="text" data-pt-id="t14" class="text-xs text-muted">Navy</p>
79
+ </div>
80
+ <div data-pt-id="c13" class="flex flex-col items-center gap-2">
81
+ <img data-pt-type="media" data-pt-id="m8" src="color-ivory.jpg" alt="Ivory" class="w-full aspect-square rounded-card object-cover">
82
+ <p data-pt-type="text" data-pt-id="t15" class="text-xs text-muted">Ivory</p>
83
+ </div>
84
+ <div data-pt-id="c14" class="flex flex-col items-center gap-2">
85
+ <img data-pt-type="media" data-pt-id="m9" src="color-grey.jpg" alt="Grey" class="w-full aspect-square rounded-card object-cover">
86
+ <p data-pt-type="text" data-pt-id="t16" class="text-xs text-muted">Grey</p>
87
+ </div>
88
+ <div data-pt-id="c15" class="flex flex-col items-center gap-2">
89
+ <img data-pt-type="media" data-pt-id="m10" data-pt-placeholder data-pt-image-prompt="レオパード柄のローファー、正方形、白背景" src="" class="w-full aspect-square rounded-card object-cover">
90
+ <p data-pt-type="text" data-pt-id="t17" class="text-xs text-muted">Leopard</p>
91
+ </div>
92
+ </div>
93
+ </div>
94
+ </section>
95
+
96
+ <section data-pt-id="s4" data-pt-name="评价" class="bg-tint py-section-y px-4">
97
+ <div data-pt-id="c16" class="mx-auto flex max-w-[1200px] flex-col gap-5">
98
+ <h2 data-pt-type="text" data-pt-id="t18" class="text-center text-display-2 font-light">お客様の声</h2>
99
+ <div data-pt-id="c17" data-pt-layout-mobile="scroll" class="grid grid-cols-1 gap-4 md:grid-cols-3">
100
+ <div data-pt-id="c18" class="flex flex-col gap-2 rounded-card bg-surface p-5">
101
+ <p data-pt-type="text" data-pt-id="t19" class="text-sm text-accent">★★★★★</p>
102
+ <p data-pt-type="text" data-pt-id="t20" class="text-sm leading-relaxed">外反母趾でも痛くならず、一日歩けました。</p>
103
+ <p data-pt-type="text" data-pt-id="t21" class="text-xs text-muted">M.K さん / 40代</p>
104
+ </div>
105
+ <div data-pt-id="c19" class="flex flex-col gap-2 rounded-card bg-surface p-5">
106
+ <p data-pt-type="text" data-pt-id="t22" class="text-sm text-accent">★★★★★</p>
107
+ <p data-pt-type="text" data-pt-id="t23" class="text-sm leading-relaxed">軽くて、旅行にも持っていきやすいです。</p>
108
+ <p data-pt-type="text" data-pt-id="t24" class="text-xs text-muted">Y.S さん / 30代</p>
109
+ </div>
110
+ <div data-pt-id="c20" class="flex flex-col gap-2 rounded-card bg-surface p-5">
111
+ <p data-pt-type="text" data-pt-id="t25" class="text-sm text-accent">★★★★☆</p>
112
+ <p data-pt-type="text" data-pt-id="t26" class="text-sm leading-relaxed">幅広なので普段より0.5cm下げてちょうどよかった。</p>
113
+ <p data-pt-type="text" data-pt-id="t27" class="text-xs text-muted">A.T さん / 50代</p>
114
+ </div>
115
+ </div>
116
+ </div>
117
+ </section>
118
+
119
+ <section data-pt-id="s5" data-pt-name="尺码表" class="py-section-y px-4">
120
+ <div data-pt-id="c21" class="mx-auto flex max-w-[1200px] flex-col gap-4">
121
+ <h2 data-pt-type="text" data-pt-id="t28" class="text-center text-display-2 font-light">サイズガイド</h2>
122
+ <table data-pt-id="tb1" data-pt-opaque="text" class="w-full border border-line text-sm">
123
+ <thead>
124
+ <tr>
125
+ <th data-pt-id="h1" class="border border-line bg-tint p-2 text-left">サイズ</th>
126
+ <th data-pt-id="h2" class="border border-line bg-tint p-2 text-left">足長 (cm)</th>
127
+ <th data-pt-id="h3" class="border border-line bg-tint p-2 text-left">足囲</th>
128
+ </tr>
129
+ </thead>
130
+ <tbody>
131
+ <tr>
132
+ <td data-pt-id="d1" class="border border-line p-2">S</td>
133
+ <td data-pt-id="d2" class="border border-line p-2">22.5 - 23.0</td>
134
+ <td data-pt-id="d3" class="border border-line p-2">2E 相当</td>
135
+ </tr>
136
+ <tr>
137
+ <td data-pt-id="d4" class="border border-line p-2">M</td>
138
+ <td data-pt-id="d5" class="border border-line p-2">23.5 - 24.0</td>
139
+ <td data-pt-id="d6" class="border border-line p-2">2E 相当</td>
140
+ </tr>
141
+ <tr>
142
+ <td data-pt-id="d7" class="border border-line p-2">L</td>
143
+ <td data-pt-id="d8" class="border border-line p-2">24.5 - 25.0</td>
144
+ <td data-pt-id="d9" class="border border-line p-2">3E 相当</td>
145
+ </tr>
146
+ </tbody>
147
+ </table>
148
+ <p data-pt-type="text" data-pt-id="t29" class="text-xs text-muted">※ 甲高の方はワンサイズ上をおすすめします。</p>
149
+ </div>
150
+ </section>
151
+
152
+ <section data-pt-id="s6" data-pt-name="关联商品" class="bg-tint py-section-y px-4">
153
+ <div data-pt-id="c22" class="mx-auto flex max-w-[1200px] flex-col gap-5">
154
+ <h2 data-pt-type="text" data-pt-id="t30" class="text-center text-display-2 font-light">あわせて見られています</h2>
155
+ <div data-pt-id="c23" data-pt-layout-mobile="scroll" class="grid grid-cols-2 gap-4 md:grid-cols-4">
156
+ <div data-pt-id="c24" class="flex flex-col gap-2">
157
+ <img data-pt-type="media" data-pt-id="m11" src="rel-samantha.jpg" alt="Samantha" class="w-full aspect-square rounded-card object-cover">
158
+ <p data-pt-type="text" data-pt-id="t31" class="text-sm">Samantha</p>
159
+ <a data-pt-type="button" data-pt-id="b2" data-pt-track="rel_1" href="#p1" class="text-sm font-bold underline">Shop Now</a>
160
+ </div>
161
+ <div data-pt-id="c25" class="flex flex-col gap-2">
162
+ <img data-pt-type="media" data-pt-id="m12" src="rel-jackie.jpg" alt="Jackie" class="w-full aspect-square rounded-card object-cover">
163
+ <p data-pt-type="text" data-pt-id="t32" class="text-sm">Jackie</p>
164
+ <a data-pt-type="button" data-pt-id="b3" data-pt-track="rel_2" href="#p2" class="text-sm font-bold underline">Shop Now</a>
165
+ </div>
166
+ <div data-pt-id="c26" class="flex flex-col gap-2">
167
+ <img data-pt-type="media" data-pt-id="m13" src="rel-michelle.jpg" alt="Michelle" class="w-full aspect-square rounded-card object-cover">
168
+ <p data-pt-type="text" data-pt-id="t33" class="text-sm">Michelle</p>
169
+ <a data-pt-type="button" data-pt-id="b4" data-pt-track="rel_3" href="#p3" class="text-sm font-bold underline">Shop Now</a>
170
+ </div>
171
+ <div data-pt-id="c27" class="flex flex-col gap-2">
172
+ <img data-pt-type="media" data-pt-id="m14" src="rel-margot.jpg" alt="Margot Walker" class="w-full aspect-square rounded-card object-cover">
173
+ <p data-pt-type="text" data-pt-id="t34" class="text-sm">Margot Walker</p>
174
+ <a data-pt-type="button" data-pt-id="b5" data-pt-track="rel_4" href="#p4" class="text-sm font-bold underline">Shop Now</a>
175
+ </div>
176
+ </div>
177
+ </div>
178
+ </section>
179
+
180
+ <section data-pt-id="s7" data-pt-name="吸底 CTA" class="py-section-y px-4">
181
+ <div data-pt-id="c28" class="mx-auto flex max-w-[1200px] flex-col gap-4">
182
+ <div data-pt-id="k1" data-pt-component="sticky-cta" data-pt-component-settings='{"label":"この商品をみる","href":"#shop","showAfter":600,"hideOnPc":false,"align":"center"}'></div>
183
+ <p data-pt-type="text" data-pt-id="t35" data-pt-hide="pc" class="text-center text-xs text-muted">送料無料・30日間返品保証</p>
184
+ </div>
185
+ </section>
186
+
187
+ </body>
188
+ </html>
@@ -0,0 +1,28 @@
1
+ # 整页 fixture
2
+
3
+ 按设计文档附录「规则来源(真实页面对照)」里那两张**真实线上页面**的形态重建的 v4 整页文档。
4
+
5
+ **是重建,不是抓取。** 两条原因:
6
+
7
+ 1. 线上页是各家自己的生产页面(非 v4 格式、零个 Tailwind v4 class)。原样放进来,
8
+ 「判定与编译一致」那条不变量会因为可编译 class 近乎为零而变成空跑。
9
+ 2. 这些 fixture **随公开 npm 包发布**(`dist/golden/fixtures/`)。把第三方商业页面的
10
+ 完整 HTML 发到公开 registry 上不合适。
11
+
12
+ 保留的是设计文档从它们身上**推导出来的规则**——那才是 fixture 要压的东西:
13
+
14
+ | fixture | 原型 | 推导出的规则(设计文档附录)| 在这里怎么体现 |
15
+ |---|---|---|---|
16
+ | `01-etvos-article-lp` | ETVOS 文章 LP | 单栏 750px;无一条媒体查询;尺寸全 `min(vw, px)` → 字号档位用 `clamp()` 流式值;64 张切图 → **图片占位与换图是主流操作** | `max-w-[750px]` 单栏贯穿;`@theme` 四个字号 / 间距 token 全是 `clamp()`;11 张图里 3 张是未落图占位;`md:` 只在两处出现 |
17
+ | `02-vivaia-campaign-lp` | VIVAIA 电商活动页 | 两端内容完全一致、差别只在呈现;新增 `data-pt-layout-mobile="scroll"`、`sticky-cta` 组件 | 无一处 `data-pt-hide="mobile"` 藏内容;三处 `data-pt-layout-mobile="scroll"`(配色 / 评价 / 关联商品);`sticky-cta` 组件;首图 `<picture>` 双 source 换图;`grid-cols-1 md:grid-cols-3` 等四处列数切换 |
18
+
19
+ 两份都带**不透明表格**(`data-pt-opaque="text"`,`td`/`th` 带 id 走 `set.text`)——
20
+ 配送方式表与尺码表。这压的是「表格是不透明叶子:结构锁定、文字可改」那条规则,
21
+ 真实页面上它也确实是这个形态。
22
+
23
+ > VIVAIA 那页取不回来(403,Cloudflare 类防护),所以它完全按设计文档附录推导出的规则
24
+ > 与那批弹窗样本里的品牌语境重建;ETVOS 那页取回了结构描述(13 区块、约 40 图、
25
+ > 单栏为主局部双栏、FAQ 手风琴、配送/手续费表格、CTA ×5 同文案),按它还原。
26
+
27
+ ⚠️ 它们证明的是「两个引擎对同一份输入产出同一串字节」与「整页级的 class 分布下判定与编译
28
+ 一致」,**不是**「我们能正确还原那两张线上页面」。
@@ -22,10 +22,15 @@
22
22
 
23
23
  两条与 golden 直接相关的性质:
24
24
 
25
- - **弹窗是 fragment,不是完整文档**(`KIND_PROFILES.popup.root === 'fragment'`),
26
- 所以这些文件没有 `<html>` / `<head>`,第一个节点就是主题块。
27
- - **`form` / `countdown` 在弹窗里是 `engage-sdk` 运行时**:源里只有声明式空壳,
28
- 行为归 Engage SDK。core 给弹窗表单出脚本会和 SDK 抢同一张表单。
25
+ - **弹窗是片段型来源,不是完整文档**:这些文件没有 `<html>` / `<head>`,第一个节点
26
+ 就是主题块。进编辑器前由 `wrap()` 规范化成合规的三层树文档(根 div 认领成
27
+ `<body>`,合成 `section` + `container` 两层),保存时 `unwrap()` 剥回来。
28
+ core 里因此**没有「弹窗档」这回事** —— 包裹之后它与落地页走同一套规则。
29
+ 往返由 `popup-wrapped/` 下的 golden 与随包发布的复算脚本一起锁住。
30
+ - **组件在源里只有声明式空壳**:`data-pt-component` + `data-pt-component-settings`,
31
+ 没有一个子元素。core 的渲染器只出语义化 markup + settings、一行脚本都不出,
32
+ **行为归宿主的运行时** —— 谁提供它(落地页的 lp-sdk / 弹窗宿主的 SDK)是宿主
33
+ 的事,core 不认识它们。
29
34
 
30
35
  每份各带自己的 `@theme`(品牌色差异很大),这同时把「按主题 hash 分缓存条目」
31
36
  与「tailwind-merge 按 @theme 派生」两条路径都压上了真实数据。
@@ -193,6 +193,132 @@
193
193
  "input": "fixtures/popup/19-jp-clinic-coupon-placeholder.html",
194
194
  "sha256": "6b56860a22d361f1cca4419ce5ff0d974fbe08cd00b92ab6c1d48e190ca4c3ef"
195
195
  },
196
+ {
197
+ "fixture": "popup-wrapped/01-jp-coupon-line-tall.txt",
198
+ "kind": "wrap",
199
+ "input": "fixtures/popup/01-jp-coupon-line-tall.html",
200
+ "sha256": "143edd4f764be4246a8349c41328c36043de5a0a4ad3cf713a0f91388d923e76"
201
+ },
202
+ {
203
+ "fixture": "popup-wrapped/02-jp-webinar-doctor.txt",
204
+ "kind": "wrap",
205
+ "input": "fixtures/popup/02-jp-webinar-doctor.html",
206
+ "sha256": "855804cd54a8ef127c05ebbbb91eb06155f07fbb86047dc29b3f9dee8dfe94cd"
207
+ },
208
+ {
209
+ "fixture": "popup-wrapped/03-jp-model-recruit.txt",
210
+ "kind": "wrap",
211
+ "input": "fixtures/popup/03-jp-model-recruit.html",
212
+ "sha256": "374800322e7423ea3e8efc37f098e839a3dfba5ceaec6c110b10d53e7599b630"
213
+ },
214
+ {
215
+ "fixture": "popup-wrapped/04-jp-shinpi-campaign.txt",
216
+ "kind": "wrap",
217
+ "input": "fixtures/popup/04-jp-shinpi-campaign.html",
218
+ "sha256": "e2904cd41c320966d58811cef77e575d5fe68c02f42d1bbe0ba14afe6d37e1f4"
219
+ },
220
+ {
221
+ "fixture": "popup-wrapped/05-jp-jal-course.txt",
222
+ "kind": "wrap",
223
+ "input": "fixtures/popup/05-jp-jal-course.html",
224
+ "sha256": "cb981b5c82094235d92080e9e551a4199765e85f43a8d1b354e873d808fb4185"
225
+ },
226
+ {
227
+ "fixture": "popup-wrapped/06-jp-training-two-cta.txt",
228
+ "kind": "wrap",
229
+ "input": "fixtures/popup/06-jp-training-two-cta.html",
230
+ "sha256": "e8366c1ad2214d60bba105a6280f59f1e7496368d3f4e74f1223eca2a8a8e9a4"
231
+ },
232
+ {
233
+ "fixture": "popup-wrapped/07-vivaia-lightweight-shoes.txt",
234
+ "kind": "wrap",
235
+ "input": "fixtures/popup/07-vivaia-lightweight-shoes.html",
236
+ "sha256": "bfaaa574c18bf400ac606b0bc75020666cc5d6ef36ce8ec3509d09be0517dce1"
237
+ },
238
+ {
239
+ "fixture": "popup-wrapped/08-sticky-black-friday-countdown.txt",
240
+ "kind": "wrap",
241
+ "input": "fixtures/popup/08-sticky-black-friday-countdown.html",
242
+ "sha256": "44dd0e8f5572e6058f8bcb6384de9dd0adb1ab1850551ccb0d55ca92c129c862"
243
+ },
244
+ {
245
+ "fixture": "popup-wrapped/09-sticky-line-coupon-countdown.txt",
246
+ "kind": "wrap",
247
+ "input": "fixtures/popup/09-sticky-line-coupon-countdown.html",
248
+ "sha256": "3f9029cc7178d259458501cf13ef3345a97bdd16c9d615887a6cda63ec5405c8"
249
+ },
250
+ {
251
+ "fixture": "popup-wrapped/10-sticky-coupon-code-countdown.txt",
252
+ "kind": "wrap",
253
+ "input": "fixtures/popup/10-sticky-coupon-code-countdown.html",
254
+ "sha256": "18fc1c6c14b9e95a544f13866f9f015079a7d8a5dfc10be65fb93318d8e1d70d"
255
+ },
256
+ {
257
+ "fixture": "popup-wrapped/11-sticky-anniversary-countdown.txt",
258
+ "kind": "wrap",
259
+ "input": "fixtures/popup/11-sticky-anniversary-countdown.html",
260
+ "sha256": "fdb886c729550a56afadf922bb3000d077a93c16829327b523cd1012df3e27d6"
261
+ },
262
+ {
263
+ "fixture": "popup-wrapped/12-vivaia-product-grid.txt",
264
+ "kind": "wrap",
265
+ "input": "fixtures/popup/12-vivaia-product-grid.html",
266
+ "sha256": "03a4229d80cc65ad2436763cc255a9919192fbbc6b01c5cde6d66515b85ad506"
267
+ },
268
+ {
269
+ "fixture": "popup-wrapped/13-vivaia-restock-split.txt",
270
+ "kind": "wrap",
271
+ "input": "fixtures/popup/13-vivaia-restock-split.html",
272
+ "sha256": "045428b2d8fb67ccce2a6f8aa19c997328cd28d3160c22a6af9a44461f5d0765"
273
+ },
274
+ {
275
+ "fixture": "popup-wrapped/14-back-to-school-email-form.txt",
276
+ "kind": "wrap",
277
+ "input": "fixtures/popup/14-back-to-school-email-form.html",
278
+ "sha256": "848803e8547c3cc4602f19f838bd1b8436efe36cbd54a84d39078173aadc0c3d"
279
+ },
280
+ {
281
+ "fixture": "popup-wrapped/15-xtool-ebook-split-form.txt",
282
+ "kind": "wrap",
283
+ "input": "fixtures/popup/15-xtool-ebook-split-form.html",
284
+ "sha256": "aebaa9a6a663507d366e05a4af0e80c4016ec7a8d5ab214b75a36da5e0e032bb"
285
+ },
286
+ {
287
+ "fixture": "popup-wrapped/16-880-off-gradient-form.txt",
288
+ "kind": "wrap",
289
+ "input": "fixtures/popup/16-880-off-gradient-form.html",
290
+ "sha256": "f1b5e87503d289edde5d4a86b481b6cef109c7c21d8a09a47f34e942f5f70fe0"
291
+ },
292
+ {
293
+ "fixture": "popup-wrapped/17-holiday-sale-price-swatch.txt",
294
+ "kind": "wrap",
295
+ "input": "fixtures/popup/17-holiday-sale-price-swatch.html",
296
+ "sha256": "f9e120f0c80988b5949f2d104bfd59537c25d848a47b0a8e86bf1754560845bf"
297
+ },
298
+ {
299
+ "fixture": "popup-wrapped/18-solar-battery-dark-specs.txt",
300
+ "kind": "wrap",
301
+ "input": "fixtures/popup/18-solar-battery-dark-specs.html",
302
+ "sha256": "83b2b13027990b25d044e71e8b63e1bfd5e79731d4a6357a6209a0c31dce45ce"
303
+ },
304
+ {
305
+ "fixture": "popup-wrapped/19-jp-clinic-coupon-placeholder.txt",
306
+ "kind": "wrap",
307
+ "input": "fixtures/popup/19-jp-clinic-coupon-placeholder.html",
308
+ "sha256": "ede1722bd495e83076c7bda4766b4a96edd9d61565d7cf5e280051b9b9ee846e"
309
+ },
310
+ {
311
+ "fixture": "page-canonical/01-etvos-article-lp.txt",
312
+ "kind": "document",
313
+ "input": "fixtures/page/01-etvos-article-lp.html",
314
+ "sha256": "8f81c7b85fab64acd8bbf38b695d9b814858de63abb879e347ed566a72de7641"
315
+ },
316
+ {
317
+ "fixture": "page-canonical/02-vivaia-campaign-lp.txt",
318
+ "kind": "document",
319
+ "input": "fixtures/page/02-vivaia-campaign-lp.html",
320
+ "sha256": "3e787c32bab758df25829d8e1631b9c9bcb43e8018b66e7e69cd66919adb3615"
321
+ },
196
322
  {
197
323
  "fixture": "apply-golden/01-set-attrs-classes.txt",
198
324
  "kind": "apply",