mosaic-headless 1.6.0 → 1.8.0
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.ja.md +2 -2
- package/README.ko.md +2 -2
- package/README.md +2 -2
- package/README.zh-TW.md +2 -2
- package/SKILL.md +12 -6
- package/assets/templates/platforms/claude-ai.json +1 -1
- package/assets/templates/platforms/claude-code.json +1 -1
- package/assets/templates/platforms/codex-cli.json +1 -1
- package/assets/templates/platforms/copilot.json +1 -1
- package/assets/templates/platforms/gemini-cli.json +1 -1
- package/bin/check-release.mjs +2 -2
- package/data/browser-verification.csv +801 -0
- package/data/design-audit.csv +3 -0
- package/data/intro-verification.csv +7 -7
- package/data/rwd-verification.csv +59 -0
- package/package.json +1 -1
- package/sites/_moksa.py +237 -4
- package/sites/moksa.json +4455 -1108
- package/tools/verify_browser.py +53 -7
package/data/design-audit.csv
CHANGED
|
@@ -1 +1,4 @@
|
|
|
1
1
|
url,breakpoints,check,level,node,detail,sample
|
|
2
|
+
https://mosaic.moksaweb.com/moksa/,"_,_t,_m",TEXT_CLIP,warn,p.M_EL206,"color is rgba(0, 0, 0, 0); painted by something else - contrast NOT checked here",01
|
|
3
|
+
https://mosaic.moksaweb.com/moksa/,"_,_t,_m",TEXT_CLIP,warn,h2.M_EL209,"color is rgba(0, 0, 0, 0); painted by background-clip:text - contrast NOT checked here","技術是拿來解決問題的,
|
|
4
|
+
不是拿來炫耀的。"
|
|
@@ -2,7 +2,7 @@ check,result,detail
|
|
|
2
2
|
PLAYS,PASS,43 of 43 intro elements changed state
|
|
3
3
|
COUNTS,PASS,--mk-n reached 100
|
|
4
4
|
ENDS,PASS,veil is out of hit-testing from 4200ms
|
|
5
|
-
NO_TRAP,PASS,"0 content elements still at opacity 0 in the viewport (
|
|
5
|
+
NO_TRAP,PASS,"0 content elements still at opacity 0 in the viewport (61 below the fold awaiting their own scroll timeline, 1 mid-blink)"
|
|
6
6
|
CLEARS,PASS,a real click landed on 'mk-caret-row'
|
|
7
7
|
DEGRADES,PASS,with reduced motion the veil is display:none throughout
|
|
8
8
|
READABLE,PASS,0 elements invisible at 120ms with reduced motion
|
|
@@ -10,13 +10,13 @@ READABLE,PASS,0 elements invisible at 120ms with reduced motion
|
|
|
10
10
|
t_ms,counter,veil_visibility,veil_clip,veil_covers_centre,hit_centre,invisible_content
|
|
11
11
|
120,,,,False,html.no-js,0
|
|
12
12
|
400,0,visible,none,True,mk-boot,7
|
|
13
|
-
800,
|
|
14
|
-
1300,
|
|
15
|
-
1800,
|
|
16
|
-
2300,
|
|
13
|
+
800,11,visible,none,True,mk-boot,7
|
|
14
|
+
1300,38,visible,none,True,mk-boot,7
|
|
15
|
+
1800,66,visible,none,True,p.M_EL56,3
|
|
16
|
+
2300,93,visible,none,True,p.M_EL56,3
|
|
17
17
|
2700,100,visible,none,True,p.M_EL56,3
|
|
18
|
-
3000,100,visible,inset(0px 0px
|
|
19
|
-
3300,100,visible,inset(0px 0px
|
|
18
|
+
3000,100,visible,inset(0px 0px 0.209424%),True,p.M_EL56,3
|
|
19
|
+
3300,100,visible,inset(0px 0px 80.1684%),False,mk-caret-row,2
|
|
20
20
|
3700,100,hidden,inset(0px 0px 100%),False,mk-caret-row,0
|
|
21
21
|
4200,100,hidden,inset(0px 0px 100%),False,mk-caret-row,0
|
|
22
22
|
5200,100,hidden,inset(0px 0px 100%),False,mk-caret-row,0
|
|
@@ -87,6 +87,46 @@ https://mosaic.moksaweb.com/moksa/,mk-svc-3,_m,grid-template-columns,"repeat(1,
|
|
|
87
87
|
https://mosaic.moksaweb.com/moksa/,mk-svc-3,_m,row-gap,10px,10px,verified
|
|
88
88
|
https://mosaic.moksaweb.com/moksa/,mk-svc-3,_m,padding-top,20px,20px,verified
|
|
89
89
|
https://mosaic.moksaweb.com/moksa/,mk-svc-3,_m,padding-bottom,20px,20px,verified
|
|
90
|
+
https://mosaic.moksaweb.com/moksa/,matrix,_t,padding-left,28px,28px,verified
|
|
91
|
+
https://mosaic.moksaweb.com/moksa/,matrix,_t,padding-right,28px,28px,verified
|
|
92
|
+
https://mosaic.moksaweb.com/moksa/,matrix,_m,padding-left,18px,18px,verified
|
|
93
|
+
https://mosaic.moksaweb.com/moksa/,matrix,_m,padding-right,18px,18px,verified
|
|
94
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-pad,_m,padding-top,52px,52px,verified
|
|
95
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-pad,_m,padding-bottom,52px,52px,verified
|
|
96
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-cols,_m,grid-template-columns,"1fr repeat(3, 46px)","1fr repeat(3, 46px)",verified
|
|
97
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-cols,_m,column-gap,6px,6px,verified
|
|
98
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-0,_m,grid-template-columns,"1fr repeat(3, 46px)","1fr repeat(3, 46px)",verified
|
|
99
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-0,_m,column-gap,6px,6px,verified
|
|
100
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-0,_m,padding-top,11px,11px,verified
|
|
101
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-0,_m,padding-bottom,11px,11px,verified
|
|
102
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-1,_m,grid-template-columns,"1fr repeat(3, 46px)","1fr repeat(3, 46px)",verified
|
|
103
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-1,_m,column-gap,6px,6px,verified
|
|
104
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-1,_m,padding-top,11px,11px,verified
|
|
105
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-1,_m,padding-bottom,11px,11px,verified
|
|
106
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-2,_m,grid-template-columns,"1fr repeat(3, 46px)","1fr repeat(3, 46px)",verified
|
|
107
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-2,_m,column-gap,6px,6px,verified
|
|
108
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-2,_m,padding-top,11px,11px,verified
|
|
109
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-2,_m,padding-bottom,11px,11px,verified
|
|
110
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-3,_m,grid-template-columns,"1fr repeat(3, 46px)","1fr repeat(3, 46px)",verified
|
|
111
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-3,_m,column-gap,6px,6px,verified
|
|
112
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-3,_m,padding-top,11px,11px,verified
|
|
113
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-3,_m,padding-bottom,11px,11px,verified
|
|
114
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-4,_m,grid-template-columns,"1fr repeat(3, 46px)","1fr repeat(3, 46px)",verified
|
|
115
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-4,_m,column-gap,6px,6px,verified
|
|
116
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-4,_m,padding-top,11px,11px,verified
|
|
117
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-4,_m,padding-bottom,11px,11px,verified
|
|
118
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-5,_m,grid-template-columns,"1fr repeat(3, 46px)","1fr repeat(3, 46px)",verified
|
|
119
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-5,_m,column-gap,6px,6px,verified
|
|
120
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-5,_m,padding-top,11px,11px,verified
|
|
121
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-5,_m,padding-bottom,11px,11px,verified
|
|
122
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-6,_m,grid-template-columns,"1fr repeat(3, 46px)","1fr repeat(3, 46px)",verified
|
|
123
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-6,_m,column-gap,6px,6px,verified
|
|
124
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-6,_m,padding-top,11px,11px,verified
|
|
125
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-6,_m,padding-bottom,11px,11px,verified
|
|
126
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-7,_m,grid-template-columns,"1fr repeat(3, 46px)","1fr repeat(3, 46px)",verified
|
|
127
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-7,_m,column-gap,6px,6px,verified
|
|
128
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-7,_m,padding-top,11px,11px,verified
|
|
129
|
+
https://mosaic.moksaweb.com/moksa/,mk-mx-r-7,_m,padding-bottom,11px,11px,verified
|
|
90
130
|
https://mosaic.moksaweb.com/moksa/,process,_t,padding-left,28px,28px,verified
|
|
91
131
|
https://mosaic.moksaweb.com/moksa/,process,_t,padding-right,28px,28px,verified
|
|
92
132
|
https://mosaic.moksaweb.com/moksa/,process,_m,padding-left,18px,18px,verified
|
|
@@ -104,6 +144,22 @@ https://mosaic.moksaweb.com/moksa/,mk-proc-2,_m,padding-top,20px,20px,verified
|
|
|
104
144
|
https://mosaic.moksaweb.com/moksa/,mk-proc-2,_m,padding-right,0px,0px,verified
|
|
105
145
|
https://mosaic.moksaweb.com/moksa/,mk-proc-3,_m,padding-top,20px,20px,verified
|
|
106
146
|
https://mosaic.moksaweb.com/moksa/,mk-proc-3,_m,padding-right,0px,0px,verified
|
|
147
|
+
https://mosaic.moksaweb.com/moksa/,figure,_t,padding-left,28px,28px,verified
|
|
148
|
+
https://mosaic.moksaweb.com/moksa/,figure,_t,padding-right,28px,28px,verified
|
|
149
|
+
https://mosaic.moksaweb.com/moksa/,figure,_m,padding-left,18px,18px,verified
|
|
150
|
+
https://mosaic.moksaweb.com/moksa/,figure,_m,padding-right,18px,18px,verified
|
|
151
|
+
https://mosaic.moksaweb.com/moksa/,mk-fig2-pad,_m,padding-top,54px,54px,verified
|
|
152
|
+
https://mosaic.moksaweb.com/moksa/,mk-fig2-pad,_m,padding-bottom,54px,54px,verified
|
|
153
|
+
https://mosaic.moksaweb.com/moksa/,mk-fig2-grid,_t,grid-template-columns,1fr 34px 1fr 34px 1fr,1fr 34px 1fr 34px 1fr,verified
|
|
154
|
+
https://mosaic.moksaweb.com/moksa/,mk-fig2-grid,_m,grid-template-columns,"repeat(1, 1fr)","repeat(1, 1fr)",verified
|
|
155
|
+
https://mosaic.moksaweb.com/moksa/,mk-fig2-grid,_m,row-gap,8px,8px,verified
|
|
156
|
+
https://mosaic.moksaweb.com/moksa/,mk-fig2-grid,_m,margin-top,24px,24px,verified
|
|
157
|
+
https://mosaic.moksaweb.com/moksa/,mk-ar-a-0,_m,transform,rotate(90deg),rotate(90deg),verified
|
|
158
|
+
https://mosaic.moksaweb.com/moksa/,mk-ar-b-0,_m,transform,rotate(90deg),rotate(90deg),verified
|
|
159
|
+
https://mosaic.moksaweb.com/moksa/,mk-ar-a-1,_m,transform,rotate(90deg),rotate(90deg),verified
|
|
160
|
+
https://mosaic.moksaweb.com/moksa/,mk-ar-b-1,_m,transform,rotate(90deg),rotate(90deg),verified
|
|
161
|
+
https://mosaic.moksaweb.com/moksa/,mk-ar-a-2,_m,transform,rotate(90deg),rotate(90deg),verified
|
|
162
|
+
https://mosaic.moksaweb.com/moksa/,mk-ar-b-2,_m,transform,rotate(90deg),rotate(90deg),verified
|
|
107
163
|
https://mosaic.moksaweb.com/moksa/,works,_t,padding-left,28px,28px,verified
|
|
108
164
|
https://mosaic.moksaweb.com/moksa/,works,_t,padding-right,28px,28px,verified
|
|
109
165
|
https://mosaic.moksaweb.com/moksa/,works,_m,padding-left,18px,18px,verified
|
|
@@ -148,6 +204,9 @@ https://mosaic.moksaweb.com/moksa/,mk-work-8,_m,grid-template-columns,1fr,1fr,ve
|
|
|
148
204
|
https://mosaic.moksaweb.com/moksa/,mk-work-8,_m,row-gap,5px,5px,verified
|
|
149
205
|
https://mosaic.moksaweb.com/moksa/,mk-work-8,_m,padding-top,15px,15px,verified
|
|
150
206
|
https://mosaic.moksaweb.com/moksa/,mk-work-8,_m,padding-bottom,15px,15px,verified
|
|
207
|
+
https://mosaic.moksaweb.com/moksa/,mk-pan,_m,padding-top,22px,22px,verified
|
|
208
|
+
https://mosaic.moksaweb.com/moksa/,mk-pan,_m,padding-bottom,22px,22px,verified
|
|
209
|
+
https://mosaic.moksaweb.com/moksa/,mk-pan-track,_m,column-gap,30px,30px,verified
|
|
151
210
|
https://mosaic.moksaweb.com/moksa/,specimen,_t,padding-left,28px,28px,verified
|
|
152
211
|
https://mosaic.moksaweb.com/moksa/,specimen,_t,padding-right,28px,28px,verified
|
|
153
212
|
https://mosaic.moksaweb.com/moksa/,specimen,_m,padding-left,18px,18px,verified
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mosaic-headless",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"description": "AI-agent skill: build Mosaic Pro (Nextend) WordPress sites by writing the underlying data model directly - 23 custom tables, no visual editor, no DOM. Every node type, style property and node property swept against a live install and asserted on the delivered HTML and compiled CSS. Installs into Claude Code, Cursor, Codex CLI, Gemini CLI, Copilot, Continue, Windsurf and Claude.ai.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"wordpress",
|
package/sites/_moksa.py
CHANGED
|
@@ -334,6 +334,15 @@ BOOT_LOG = [
|
|
|
334
334
|
]
|
|
335
335
|
RULER_TICKS = 13
|
|
336
336
|
|
|
337
|
+
# FIGURE 01's rows. Up here with the other tables rather than beside the section
|
|
338
|
+
# that draws it, because motion_css() reads it to build the arrow stagger and is
|
|
339
|
+
# defined long before the figure is.
|
|
340
|
+
FLOW = [
|
|
341
|
+
("網站表單", "FORM", "n8n 節點", "WORKFLOW", "ERP 建單", "ERP"),
|
|
342
|
+
("電商訂單", "ORDER", "Claude 判讀", "REASONING", "通知/報表", "NOTIFY"),
|
|
343
|
+
("客服訊息", "INBOX", "分類與派工", "TRIAGE", "CRM 紀錄", "CRM"),
|
|
344
|
+
]
|
|
345
|
+
|
|
337
346
|
CLAUSES = [
|
|
338
347
|
("01", "SERVICES", "#services", "--mk-s1"),
|
|
339
348
|
("02", "PROCESS", "#process", "--mk-s2"),
|
|
@@ -358,6 +367,12 @@ REVEALS = [
|
|
|
358
367
|
("mk-work-4", 3), ("mk-work-5", 3), ("mk-work-6", 4), ("mk-work-7", 4),
|
|
359
368
|
("mk-work-8", 5),
|
|
360
369
|
("mk-plate-num", 0), ("mk-plate-t", 1),
|
|
370
|
+
("mk-mx-head", 0), ("mk-mx-cols", 1),
|
|
371
|
+
("mk-mx-r-0", 1), ("mk-mx-r-1", 2), ("mk-mx-r-2", 2), ("mk-mx-r-3", 3),
|
|
372
|
+
("mk-mx-r-4", 3), ("mk-mx-r-5", 4), ("mk-mx-r-6", 4), ("mk-mx-r-7", 5),
|
|
373
|
+
("mk-fig2-head", 0), ("mk-fl-a-0", 1), ("mk-fl-b-0", 2), ("mk-fl-c-0", 3),
|
|
374
|
+
("mk-fl-a-1", 2), ("mk-fl-b-1", 3), ("mk-fl-c-1", 4),
|
|
375
|
+
("mk-fl-a-2", 3), ("mk-fl-b-2", 4), ("mk-fl-c-2", 5),
|
|
361
376
|
("mk-spec2-head", 0), ("mk-spec2-r-0", 1), ("mk-spec2-r-1", 2),
|
|
362
377
|
("mk-spec2-r-2", 3), ("mk-spec2-r-3", 4),
|
|
363
378
|
("mk-stack-head", 0), ("mk-stack-0", 1), ("mk-stack-1", 2), ("mk-stack-2", 3),
|
|
@@ -390,6 +405,12 @@ def motion_css():
|
|
|
390
405
|
for i, (_g, items) in enumerate(STACK)
|
|
391
406
|
for j in range(len(items)))
|
|
392
407
|
marks = ",".join("#mk-mark-%d" % i for i in range(4))
|
|
408
|
+
# the diagram's arrows, and the delay that makes the pulse travel down it
|
|
409
|
+
flow_arrows = ",".join("#mk-ar-%s-%d>*" % (c, i)
|
|
410
|
+
for i in range(len(FLOW)) for c in ("a", "b"))
|
|
411
|
+
flow_stagger = "\n".join(
|
|
412
|
+
" #mk-ar-%s-%d>*{animation-delay:%dms}" % (c, i, i * 260 + j * 130)
|
|
413
|
+
for i in range(len(FLOW)) for j, c in enumerate(("a", "b")))
|
|
393
414
|
# the boot sequence, phase by phase: the clauses report in, the log writes
|
|
394
415
|
# itself out a line at a time, and the ruler ticks up the left margin
|
|
395
416
|
boot_stagger = "\n".join(
|
|
@@ -455,6 +476,15 @@ def motion_css():
|
|
|
455
476
|
"@keyframes mk-idxon{from,to{color:rgb(255,90,54);"
|
|
456
477
|
"letter-spacing:.2em}}",
|
|
457
478
|
"@keyframes mk-dotin{from{transform:scale(0)}to{transform:scale(1)}}",
|
|
479
|
+
# the accent band pans sideways while it crosses the viewport
|
|
480
|
+
"@keyframes mk-pan{from{transform:translateX(2%)}"
|
|
481
|
+
"to{transform:translateX(-34%)}}",
|
|
482
|
+
# a headline that fills with ink as it is read past
|
|
483
|
+
"@keyframes mk-fillscrub{from{background-position:100% 0}"
|
|
484
|
+
"to{background-position:0 0}}",
|
|
485
|
+
# a pulse travelling down the diagram's arrows
|
|
486
|
+
"@keyframes mk-flow{0%{transform:translateX(-7px);opacity:.2}"
|
|
487
|
+
"45%{opacity:1}100%{transform:translateX(7px);opacity:.2}}",
|
|
458
488
|
|
|
459
489
|
# ── the document frame ───────────────────────────────────────────────
|
|
460
490
|
# A spec sheet has margins. These two fixed rules are what say the content
|
|
@@ -522,8 +552,14 @@ def motion_css():
|
|
|
522
552
|
|
|
523
553
|
# the clause index. It needs room outside the document margin, so it only
|
|
524
554
|
# appears once the viewport is wide enough to have that room.
|
|
525
|
-
|
|
526
|
-
|
|
555
|
+
# The index is FIXED, so it does not sit on the ground it was declared
|
|
556
|
+
# against - it sits on whatever has scrolled underneath. Over the accent
|
|
557
|
+
# band its faint grey became unreadable. Giving it its own paper ground
|
|
558
|
+
# means it carries its background with it instead of borrowing one.
|
|
559
|
+
"#mk-index{position:fixed;left:8px;top:50%;transform:translateY(-50%);"
|
|
560
|
+
"z-index:3;display:grid;row-gap:11px;pointer-events:auto;"
|
|
561
|
+
"background:rgb(250,250,247);padding:14px 10px;"
|
|
562
|
+
"border:1px solid " + RULE + "}",
|
|
527
563
|
"@media (max-width:1439px){#mk-index{display:none}}",
|
|
528
564
|
|
|
529
565
|
# process: the sequence marker on each step, and the line it sits on
|
|
@@ -692,6 +728,8 @@ def motion_css():
|
|
|
692
728
|
" #mk-mast-meta{animation-delay:3.06s}",
|
|
693
729
|
" #mk-mast-lede{animation-delay:3.54s}",
|
|
694
730
|
" #mk-mast-cta{animation-delay:3.66s}",
|
|
731
|
+
" " + flow_arrows + "{animation:mk-flow 1.9s ease-in-out infinite}",
|
|
732
|
+
flow_stagger,
|
|
695
733
|
" #mk-caret{animation:mk-caret 1.15s steps(1,end) infinite;"
|
|
696
734
|
"animation-delay:3.84s}",
|
|
697
735
|
" #mk-mast-rule{animation:mk-drawx .9s cubic-bezier(.2,.7,.3,1) forwards;"
|
|
@@ -725,6 +763,28 @@ def motion_css():
|
|
|
725
763
|
# the sequence markers pop as each step arrives
|
|
726
764
|
" " + proc_dots + "{animation:mk-dotin .01s linear both;"
|
|
727
765
|
"animation-timeline:view();animation-range:entry 14% cover 26%}",
|
|
766
|
+
# the band pans across the whole time it is on screen
|
|
767
|
+
" #mk-pan-track{animation:mk-pan linear both;"
|
|
768
|
+
"animation-timeline:view();animation-range:cover 0% cover 100%}",
|
|
769
|
+
# The statement fills with ink as it passes. The gradient is built so the
|
|
770
|
+
# UNANIMATED state shows the faint half - `color:transparent` plus
|
|
771
|
+
# `background-clip:text` is one of the few ways to make text that is
|
|
772
|
+
# genuinely invisible if its animation never runs, and the page must never
|
|
773
|
+
# depend on that. At `from` the visible half is the faint colour, so the
|
|
774
|
+
# worst case is a paler headline, not a missing one.
|
|
775
|
+
" #mk-plate-t h2{background-image:linear-gradient(90deg,"
|
|
776
|
+
# The unfilled half is `--mk-faint`, not a decorative pale grey. At
|
|
777
|
+
# rgb(168,170,176) the tail of the line measured 2.3:1 against the panel
|
|
778
|
+
# while the scrub had not reached it - readable only once you had scrolled
|
|
779
|
+
# far enough, which is not a thing to ask of a sentence. This value clears
|
|
780
|
+
# 4.66:1 on its own, so the effect is a change in weight rather than a
|
|
781
|
+
# change between legible and not.
|
|
782
|
+
"rgb(22,24,28) 0 50%,rgb(107,109,113) 50% 100%);"
|
|
783
|
+
"background-size:220% 100%;background-position:100% 0;"
|
|
784
|
+
"-webkit-background-clip:text;background-clip:text;"
|
|
785
|
+
"color:rgba(0,0,0,0);"
|
|
786
|
+
"animation:mk-fillscrub linear both;animation-timeline:view();"
|
|
787
|
+
"animation-range:entry 24% cover 58%}",
|
|
728
788
|
" }",
|
|
729
789
|
" @supports (timeline-scope:--x){",
|
|
730
790
|
# the index entry lights while its own section is on screen. No fill mode, so
|
|
@@ -1337,6 +1397,178 @@ CONTACT = section("contact", [wrap("mk-contact-in", [
|
|
|
1337
1397
|
]),
|
|
1338
1398
|
])])
|
|
1339
1399
|
|
|
1400
|
+
# ── the pan strip ─────────────────────────────────────────────────────────────
|
|
1401
|
+
# The page has no field of colour anywhere - the accent has only ever been a rule, a
|
|
1402
|
+
# mark or a small block. A band of it, with the whole line panning sideways as you
|
|
1403
|
+
# scroll down, is the one moment here that is purely about being looked at. Ink on
|
|
1404
|
+
# the accent measures 5.73:1, which is why the type is dark rather than light.
|
|
1405
|
+
PAN_WORDS = ["WEB", "COMMERCE", "AUTOMATION", "AI", "ERP", "SEO", "SOFTWARE"]
|
|
1406
|
+
|
|
1407
|
+
PAN = box("mk-pan",
|
|
1408
|
+
{"backgroundColor": {"token": "--mk-accent"},
|
|
1409
|
+
"paddingTop": "34px", "paddingBottom": "34px",
|
|
1410
|
+
"customStyles": "overflow:hidden;"},
|
|
1411
|
+
_m={"paddingTop": "22px", "paddingBottom": "22px"},
|
|
1412
|
+
children=[
|
|
1413
|
+
box("mk-pan-track",
|
|
1414
|
+
{"display": "flex", "columnGap": "56px", "alignItems": "baseline",
|
|
1415
|
+
"customStyles": "white-space:nowrap;width:max-content;"},
|
|
1416
|
+
_m={"columnGap": "30px"},
|
|
1417
|
+
children=[
|
|
1418
|
+
node for i, w in enumerate(PAN_WORDS) for node in (
|
|
1419
|
+
box("mk-pan-n-%d" % i, {},
|
|
1420
|
+
[mono("%02d" % (i + 1), size="11px",
|
|
1421
|
+
color="rgb(22,24,28)", track="0.18em")]),
|
|
1422
|
+
box("mk-pan-w-%d" % i, {},
|
|
1423
|
+
[T("p", w, color={"token": "--mk-ink"},
|
|
1424
|
+
fontSize="58px", fontWeight="600",
|
|
1425
|
+
fontFamily=DISPLAY, letterSpacing="-0.02em",
|
|
1426
|
+
lineHeight="1",
|
|
1427
|
+
_t={"fontSize": "44px"},
|
|
1428
|
+
_m={"fontSize": "31px"})]),
|
|
1429
|
+
)
|
|
1430
|
+
]),
|
|
1431
|
+
])
|
|
1432
|
+
|
|
1433
|
+
|
|
1434
|
+
# ── the capability matrix ─────────────────────────────────────────────────────
|
|
1435
|
+
# The page could tell you what the studio does; a specification SHOWS you, in a form
|
|
1436
|
+
# you can read across. Filled, half and hollow marks carry the whole answer without
|
|
1437
|
+
# a sentence, and the density is the point - this is the one block on the page that
|
|
1438
|
+
# rewards being read as a grid rather than as a line.
|
|
1439
|
+
MATRIX_COLS = ["網站", "電商", "自動化"]
|
|
1440
|
+
MATRIX = [
|
|
1441
|
+
("客製化設計", "BESPOKE DESIGN", "●", "●", "◐"),
|
|
1442
|
+
("RWD 三段點", "RESPONSIVE", "●", "●", "○"),
|
|
1443
|
+
("SEO 結構化資料", "STRUCTURED DATA", "●", "●", "○"),
|
|
1444
|
+
("金流/物流串接", "PAYMENT / LOGISTICS", "○", "●", "◐"),
|
|
1445
|
+
("會員與訂單系統", "ACCOUNTS / ORDERS", "○", "●", "◐"),
|
|
1446
|
+
("n8n 流程自動化", "WORKFLOW AUTOMATION", "◐", "◐", "●"),
|
|
1447
|
+
("AI 內容與判讀", "AI CONTENT / REASONING", "◐", "◐", "●"),
|
|
1448
|
+
("上線後維運", "MAINTENANCE", "●", "●", "●"),
|
|
1449
|
+
]
|
|
1450
|
+
|
|
1451
|
+
|
|
1452
|
+
def mark(sym, i, j):
|
|
1453
|
+
"""One cell of the matrix. The symbol carries the meaning; the colour only
|
|
1454
|
+
reinforces it, so it still reads if the colour is gone."""
|
|
1455
|
+
colour = "--mk-ink" if sym == "●" else (
|
|
1456
|
+
"--mk-accent-ink" if sym == "◐" else "--mk-faint")
|
|
1457
|
+
return box("mk-mx-c-%d-%d" % (i, j),
|
|
1458
|
+
{"display": "flex", "justifyContent": "center"},
|
|
1459
|
+
[T("p", sym, color={"token": colour}, fontSize="13px",
|
|
1460
|
+
fontFamily=MONO, lineHeight="1")])
|
|
1461
|
+
|
|
1462
|
+
|
|
1463
|
+
MATRIX_SEC = section("matrix", [wrap("mk-mx-in", [
|
|
1464
|
+
box("mk-mx-pad", {"paddingTop": "78px", "paddingBottom": "78px"},
|
|
1465
|
+
_m={"paddingTop": "52px", "paddingBottom": "52px"},
|
|
1466
|
+
children=[
|
|
1467
|
+
box("mk-mx-head",
|
|
1468
|
+
{"display": "flex", "justifyContent": "space-between",
|
|
1469
|
+
"columnGap": "20px", "rowGap": "8px", "alignItems": "baseline",
|
|
1470
|
+
"customStyles": "flex-wrap:wrap;"},
|
|
1471
|
+
[mono("TABLE 01 — CAPABILITY", size="10px",
|
|
1472
|
+
color="--mk-accent-ink", track="0.22em"),
|
|
1473
|
+
mono("● 標準 ◐ 選配 ○ 不含", size="10px",
|
|
1474
|
+
color="--mk-faint", track="0.12em")]),
|
|
1475
|
+
# the column header, then one row per capability
|
|
1476
|
+
box("mk-mx-cols",
|
|
1477
|
+
{"display": "grid", "gridCols": "1fr repeat(3, 78px)",
|
|
1478
|
+
"columnGap": "10px", "alignItems": "end", "marginTop": "26px",
|
|
1479
|
+
"paddingBottom": "10px",
|
|
1480
|
+
"customStyles": "border-bottom:1px solid " + RULE_INK + ";"},
|
|
1481
|
+
_m={"gridCols": "1fr repeat(3, 46px)", "columnGap": "6px"},
|
|
1482
|
+
children=[mono("CAPABILITY", size="10px", color="--mk-faint",
|
|
1483
|
+
track="0.18em")]
|
|
1484
|
+
+ [box("mk-mx-h-%d" % j,
|
|
1485
|
+
{"display": "flex", "justifyContent": "center"},
|
|
1486
|
+
[mono(c, size="11px", color="--mk-ink", track="0.06em")])
|
|
1487
|
+
for j, c in enumerate(MATRIX_COLS)]),
|
|
1488
|
+
box("mk-mx-rows", {}, [
|
|
1489
|
+
box("mk-mx-r-%d" % i,
|
|
1490
|
+
{"display": "grid", "gridCols": "1fr repeat(3, 78px)",
|
|
1491
|
+
"columnGap": "10px", "alignItems": "center",
|
|
1492
|
+
"paddingTop": "13px", "paddingBottom": "13px",
|
|
1493
|
+
"customStyles": "border-bottom:1px solid " + RULE + ";"},
|
|
1494
|
+
_m={"gridCols": "1fr repeat(3, 46px)", "columnGap": "6px",
|
|
1495
|
+
"paddingTop": "11px", "paddingBottom": "11px"},
|
|
1496
|
+
children=[
|
|
1497
|
+
box("mk-mx-n-%d" % i,
|
|
1498
|
+
{"display": "flex", "columnGap": "12px",
|
|
1499
|
+
"alignItems": "baseline", "rowGap": "2px",
|
|
1500
|
+
"customStyles": "flex-wrap:wrap;"},
|
|
1501
|
+
[T("p", zh, color={"token": "--mk-ink"},
|
|
1502
|
+
fontSize="14px", _m={"fontSize": "12.5px"}),
|
|
1503
|
+
mono(en, size="9px", color="--mk-faint",
|
|
1504
|
+
track="0.16em", _m={"display": "none"})]),
|
|
1505
|
+
] + [mark(sym, i, j) for j, sym in enumerate(marks)])
|
|
1506
|
+
for i, (zh, en, *marks) in enumerate(MATRIX)
|
|
1507
|
+
]),
|
|
1508
|
+
]),
|
|
1509
|
+
])], bg="--mk-panel")
|
|
1510
|
+
|
|
1511
|
+
|
|
1512
|
+
# ── FIGURE 01: what a workflow actually looks like ────────────────────────────
|
|
1513
|
+
# The page had no graphic on it at all - every section was type and rules, which is
|
|
1514
|
+
# austere for eleven screens. A diagram is the one element that says something no
|
|
1515
|
+
# sentence here can, and drawn in hairline boxes it belongs to the same document
|
|
1516
|
+
# rather than arriving from a different one.
|
|
1517
|
+
|
|
1518
|
+
|
|
1519
|
+
def flow_cell(attr, zh, en):
|
|
1520
|
+
return box(attr,
|
|
1521
|
+
{"paddingTop": "14px", "paddingBottom": "14px",
|
|
1522
|
+
"paddingLeft": "16px", "paddingRight": "16px",
|
|
1523
|
+
"customStyles": "border:1px solid " + RULE + ";"},
|
|
1524
|
+
[mono(en, size="9px", color="--mk-faint", track="0.18em"),
|
|
1525
|
+
T("p", zh, color={"token": "--mk-ink"}, fontSize="14px",
|
|
1526
|
+
marginTop="6px", _m={"fontSize": "13px"})])
|
|
1527
|
+
|
|
1528
|
+
|
|
1529
|
+
def arrow(attr):
|
|
1530
|
+
return box(attr, {"display": "flex", "justifyContent": "center",
|
|
1531
|
+
"alignItems": "center"},
|
|
1532
|
+
[T("p", "→", color={"token": "--mk-accent-ink"}, fontSize="15px",
|
|
1533
|
+
fontFamily=MONO)],
|
|
1534
|
+
_m={"customStyles": "transform:rotate(90deg);"})
|
|
1535
|
+
|
|
1536
|
+
|
|
1537
|
+
FIGURE_SEC = section("figure", [wrap("mk-fig2-in", [
|
|
1538
|
+
box("mk-fig2-pad", {"paddingTop": "82px", "paddingBottom": "82px"},
|
|
1539
|
+
_m={"paddingTop": "54px", "paddingBottom": "54px"},
|
|
1540
|
+
children=[
|
|
1541
|
+
box("mk-fig2-head",
|
|
1542
|
+
{"display": "flex", "justifyContent": "space-between",
|
|
1543
|
+
"columnGap": "20px", "rowGap": "8px",
|
|
1544
|
+
"customStyles": "flex-wrap:wrap;"},
|
|
1545
|
+
[mono("FIGURE 01 — A WORKFLOW", size="10px",
|
|
1546
|
+
color="--mk-accent-ink", track="0.22em"),
|
|
1547
|
+
mono("INPUT → PROCESS → OUTPUT", size="10px",
|
|
1548
|
+
color="--mk-faint", track="0.18em")]),
|
|
1549
|
+
box("mk-fig2-grid",
|
|
1550
|
+
{"display": "grid", "gridCols": "1fr 44px 1fr 44px 1fr",
|
|
1551
|
+
"columnGap": "0px", "rowGap": "16px", "marginTop": "34px"},
|
|
1552
|
+
_t={"gridCols": "1fr 34px 1fr 34px 1fr"},
|
|
1553
|
+
_m={"gridCols": "repeat(1, 1fr)", "rowGap": "8px",
|
|
1554
|
+
"marginTop": "24px"},
|
|
1555
|
+
children=[
|
|
1556
|
+
node for i, row in enumerate(FLOW) for node in (
|
|
1557
|
+
flow_cell("mk-fl-a-%d" % i, row[0], row[1]),
|
|
1558
|
+
arrow("mk-ar-a-%d" % i),
|
|
1559
|
+
flow_cell("mk-fl-b-%d" % i, row[2], row[3]),
|
|
1560
|
+
arrow("mk-ar-b-%d" % i),
|
|
1561
|
+
flow_cell("mk-fl-c-%d" % i, row[4], row[5]),
|
|
1562
|
+
)
|
|
1563
|
+
]),
|
|
1564
|
+
T("p", "同一條線可以接任何一端:表單、訂單、訊息進來,"
|
|
1565
|
+
"判斷與轉換在中間,結果送到你本來就在用的系統裡。",
|
|
1566
|
+
color={"token": "--mk-muted"}, fontSize="13px", lineHeight="1.95",
|
|
1567
|
+
marginTop="26px", maxWidth="42em", _m={"fontSize": "12.5px"}),
|
|
1568
|
+
]),
|
|
1569
|
+
])])
|
|
1570
|
+
|
|
1571
|
+
|
|
1340
1572
|
# ── PLATE 01: a statement ─────────────────────────────────────────────────────
|
|
1341
1573
|
# Every clause on this page has the same rhythm - a numbered head, then hairline
|
|
1342
1574
|
# rows. That is correct for a specification and monotonous for a page. A document
|
|
@@ -1511,8 +1743,9 @@ HOME_TREE = {"type": "div", "data": {"attrID": "mk-home"},
|
|
|
1511
1743
|
MARKS, INDEX, CUE,
|
|
1512
1744
|
# paper, panel, paper, ink, paper, ink, paper - the page
|
|
1513
1745
|
# changes ground five times so it reads as chapters
|
|
1514
|
-
MASTHEAD, TICKER_BAND, PLATE, SERVICE_SEC,
|
|
1515
|
-
|
|
1746
|
+
MASTHEAD, TICKER_BAND, PLATE, SERVICE_SEC, MATRIX_SEC,
|
|
1747
|
+
PROCESS_SEC, FIGURE_SEC, WORK_SEC, PAN, SPECIMEN, STACK_SEC,
|
|
1748
|
+
PRODUCT_SEC, VOICE_SEC, CONTACT,
|
|
1516
1749
|
])]}
|
|
1517
1750
|
|
|
1518
1751
|
SITE = {
|