@0001-labs/design-system-1 0.3.0-alpha.28 → 0.3.1
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/DS1/0-face/button-label.ts +28 -0
- package/DS1/0-face/calendar.ts +6 -4
- package/DS1/0-face/carousel.ts +70 -0
- package/DS1/0-face/constraint-panel.ts +443 -0
- package/DS1/0-face/constraint-site.ts +201 -0
- package/DS1/0-face/constraints.ts +308 -0
- package/DS1/0-face/copy-text.ts +1 -1
- package/DS1/0-face/cut-fade.ts +312 -0
- package/DS1/0-face/define.ts +30 -2
- package/DS1/0-face/face-correction.ts +96 -0
- package/DS1/0-face/failures.ts +32 -0
- package/DS1/0-face/fault-lines.ts +159 -0
- package/DS1/0-face/feedback-overlays.ts +5 -3
- package/DS1/0-face/feedback.ts +3 -0
- package/DS1/0-face/focus-vision.swift +31 -0
- package/DS1/0-face/focus.ts +181 -0
- package/DS1/0-face/google-face-metrics.json +102 -0
- package/DS1/0-face/grid.ts +61 -4
- package/DS1/0-face/i18n.ts +36 -18
- package/DS1/0-face/layout-inspector.ts +124 -98
- package/DS1/0-face/preference-options.ts +4 -0
- package/DS1/0-face/scaling.ts +21 -3
- package/DS1/0-face/shape.ts +43 -0
- package/DS1/0-face/text-contrast.ts +164 -0
- package/DS1/0-face/theme-bootstrap.js +43 -0
- package/DS1/0-face/toast-queue.ts +7 -1
- package/DS1/0-face/translations/da.json +10 -1
- package/DS1/0-face/translations/de.json +10 -1
- package/DS1/0-face/translations/en.json +10 -1
- package/DS1/0-face/translations/es.json +10 -1
- package/DS1/0-face/translations/fi.json +10 -1
- package/DS1/0-face/translations/fr.json +10 -1
- package/DS1/0-face/translations/is.json +10 -1
- package/DS1/0-face/translations/it.json +10 -1
- package/DS1/0-face/translations/ja.json +10 -1
- package/DS1/0-face/translations/ko.json +10 -1
- package/DS1/0-face/translations/nb.json +10 -1
- package/DS1/0-face/translations/pt-BR.json +10 -1
- package/DS1/0-face/translations/pt.json +10 -1
- package/DS1/0-face/translations/sv.json +10 -1
- package/DS1/0-face/translations/zh.json +10 -1
- package/DS1/0-face/word-wrap.ts +113 -0
- package/DS1/1-root/card.css +47 -20
- package/DS1/1-root/carousel.css +7 -0
- package/DS1/1-root/cjk.css +1 -1
- package/DS1/1-root/component-tokens.css +3 -2
- package/DS1/1-root/context.css +44 -6
- package/DS1/1-root/controls.css +99 -8
- package/DS1/1-root/document.css +9 -1
- package/DS1/1-root/empty.css +46 -5
- package/DS1/1-root/figures.css +50 -3
- package/DS1/1-root/fonts.css +1 -1
- package/DS1/1-root/footer.css +147 -16
- package/DS1/1-root/foundation.ts +60 -0
- package/DS1/1-root/guide.css +35 -0
- package/DS1/1-root/header.css +30 -9
- package/DS1/1-root/material.css +67 -0
- package/DS1/1-root/notice.css +10 -2
- package/DS1/1-root/one.css +15 -7
- package/DS1/1-root/progress.css +9 -1
- package/DS1/1-root/row.css +68 -2
- package/DS1/1-root/table.css +47 -4
- package/DS1/1-root/text.css +26 -0
- package/DS1/2-core/button-split.ts +10 -5
- package/DS1/2-core/chart/chart.css +71 -2
- package/DS1/2-core/chart/grid-chart.ts +2 -1
- package/DS1/2-core/ds-activity.ts +32 -0
- package/DS1/2-core/ds-app-chrome.ts +1 -0
- package/DS1/2-core/ds-avatar.ts +75 -7
- package/DS1/2-core/ds-badge.ts +8 -1
- package/DS1/2-core/ds-button.ts +7 -1
- package/DS1/2-core/ds-chart.ts +184 -49
- package/DS1/2-core/ds-cycle.ts +27 -186
- package/DS1/2-core/ds-device-bezel.ts +1 -0
- package/DS1/2-core/ds-empty.ts +6 -4
- package/DS1/2-core/ds-input.ts +23 -3
- package/DS1/2-core/ds-message.ts +22 -0
- package/DS1/2-core/ds-number.ts +349 -0
- package/DS1/2-core/ds-progress.ts +3 -0
- package/DS1/2-core/ds-radio.ts +3 -0
- package/DS1/2-core/ds-stat.ts +3 -0
- package/DS1/2-core/ds-switch.ts +5 -1
- package/DS1/2-core/ds-tag.ts +9 -2
- package/DS1/2-core/ds-text.ts +7 -0
- package/DS1/2-core/ds-token.ts +3 -0
- package/DS1/2-core/ds-tooltip.ts +17 -2
- package/DS1/2-core/ds-window.ts +1 -0
- package/DS1/2-core/generated/ds-icon-map.ts +47 -5
- package/DS1/2-core/generated/icons-extra.ts +24 -3
- package/DS1/2-core/generated/icons-immediate.ts +3 -3
- package/DS1/2-core/number-expression.ts +64 -0
- package/DS1/2-core/styles/ds-activity.css +16 -0
- package/DS1/2-core/styles/ds-badge.css +4 -1
- package/DS1/2-core/styles/ds-button-host.css +47 -13
- package/DS1/2-core/styles/ds-button-split.css +8 -0
- package/DS1/2-core/styles/ds-button.css +94 -20
- package/DS1/2-core/styles/ds-card.css +6 -0
- package/DS1/2-core/styles/ds-checkbox.css +20 -8
- package/DS1/2-core/styles/ds-image.css +2 -1
- package/DS1/2-core/styles/ds-input.css +37 -4
- package/DS1/2-core/styles/ds-number.css +127 -0
- package/DS1/2-core/styles/ds-radio.css +4 -1
- package/DS1/2-core/styles/ds-switch.css +34 -4
- package/DS1/2-core/styles/ds-tag.css +34 -6
- package/DS1/2-core/styles/ds-text.css +13 -4
- package/DS1/2-core/styles/ds-token.css +4 -1
- package/DS1/2-core/styles/ds-tooltip.css +13 -7
- package/DS1/3-unit/calendar-base.ts +5 -2
- package/DS1/3-unit/context-trigger.ts +29 -2
- package/DS1/3-unit/ds-calendar.ts +711 -37
- package/DS1/3-unit/ds-carousel.ts +13 -1
- package/DS1/3-unit/ds-context.ts +40 -2
- package/DS1/3-unit/ds-form.ts +47 -11
- package/DS1/3-unit/ds-list.ts +31 -3
- package/DS1/3-unit/ds-matrix.ts +3 -0
- package/DS1/3-unit/ds-menu.ts +9 -5
- package/DS1/3-unit/ds-row.ts +17 -2
- package/DS1/3-unit/ds-select.ts +1 -1
- package/DS1/3-unit/ds-sidebar.ts +18 -3
- package/DS1/3-unit/ds-table-of-contents.ts +166 -0
- package/DS1/3-unit/ds-table.ts +2 -1
- package/DS1/3-unit/ds-tabs.ts +40 -2
- package/DS1/3-unit/styles/ds-calendar.css +356 -42
- package/DS1/3-unit/styles/ds-context.css +38 -0
- package/DS1/3-unit/styles/ds-list.css +26 -1
- package/DS1/3-unit/styles/ds-matrix.css +4 -1
- package/DS1/3-unit/styles/ds-row.css +39 -0
- package/DS1/3-unit/styles/ds-select.css +22 -12
- package/DS1/3-unit/styles/ds-sidebar.css +16 -2
- package/DS1/3-unit/styles/ds-table-of-contents.css +40 -0
- package/DS1/3-unit/styles/ds-tabs.css +95 -0
- package/DS1/4-page/ds-container.ts +2 -1
- package/DS1/4-page/ds-document.ts +3 -0
- package/DS1/4-page/ds-footer.ts +93 -18
- package/DS1/4-page/ds-header.ts +5 -1
- package/DS1/4-page/ds-overlay.ts +8 -3
- package/DS1/4-page/ds-page.ts +22 -1
- package/DS1/4-page/email.ts +7 -4
- package/DS1/4-page/styles/ds-container.css +0 -1
- package/DS1/4-page/styles/ds-overlay.css +7 -0
- package/DS1/4-page/styles/ds-page.css +10 -0
- package/DS1/5-visual-kit/app-chrome.ts +129 -0
- package/DS1/5-visual-kit/bezels.ts +394 -0
- package/DS1/5-visual-kit/browser-icons.LICENSE +53 -0
- package/DS1/5-visual-kit/browser-icons.ts +47 -0
- package/DS1/5-visual-kit/browser-styles.ts +53 -0
- package/DS1/5-visual-kit/device-bezel.ts +55 -0
- package/DS1/5-visual-kit/instagram-frame.ts +15 -0
- package/DS1/5-visual-kit/instagram.ts +41 -0
- package/DS1/5-visual-kit/mobile-chrome.ts +25 -0
- package/DS1/5-visual-kit/native-icon-paths.ts +145 -0
- package/DS1/5-visual-kit/window.ts +22 -0
- package/DS1/components.ts +1 -0
- package/DS1/index.ts +14 -1
- package/DS1/react/choice.ts +4 -2
- package/DS1/react/context.ts +11 -6
- package/DS1/react/empty.ts +2 -2
- package/DS1/react/footer.ts +5 -5
- package/DS1/react/header.ts +1 -1
- package/DS1/react/message.ts +4 -2
- package/DS1/react/number.ts +53 -0
- package/DS1/react/sidebar.ts +4 -2
- package/DS1/react/table-of-contents.ts +28 -0
- package/DS1/x-flag/LICENSE.md +26 -0
- package/DS1/x-flag/ac.svg +1 -0
- package/DS1/x-flag/ad.svg +1 -0
- package/DS1/x-flag/ae.svg +1 -0
- package/DS1/x-flag/af.svg +1 -0
- package/DS1/x-flag/ag.svg +1 -0
- package/DS1/x-flag/ai.svg +1 -0
- package/DS1/x-flag/al.svg +1 -0
- package/DS1/x-flag/am.svg +1 -0
- package/DS1/x-flag/ao.svg +1 -0
- package/DS1/x-flag/aq.svg +1 -0
- package/DS1/x-flag/ar.svg +1 -0
- package/DS1/x-flag/as.svg +1 -0
- package/DS1/x-flag/at.svg +1 -0
- package/DS1/x-flag/au.svg +1 -0
- package/DS1/x-flag/aw.svg +1 -0
- package/DS1/x-flag/ax.svg +1 -0
- package/DS1/x-flag/az.svg +1 -0
- package/DS1/x-flag/ba.svg +1 -0
- package/DS1/x-flag/bb.svg +1 -0
- package/DS1/x-flag/bd.svg +1 -0
- package/DS1/x-flag/be.svg +1 -0
- package/DS1/x-flag/bf.svg +1 -0
- package/DS1/x-flag/bg.svg +1 -0
- package/DS1/x-flag/bh.svg +1 -0
- package/DS1/x-flag/bi.svg +1 -0
- package/DS1/x-flag/bj.svg +1 -0
- package/DS1/x-flag/bl.svg +1 -0
- package/DS1/x-flag/bm.svg +1 -0
- package/DS1/x-flag/bn.svg +1 -0
- package/DS1/x-flag/bo.svg +1 -0
- package/DS1/x-flag/bq-bo.svg +1 -0
- package/DS1/x-flag/bq-sa.svg +1 -0
- package/DS1/x-flag/bq-se.svg +1 -0
- package/DS1/x-flag/bq.svg +1 -0
- package/DS1/x-flag/br.svg +1 -0
- package/DS1/x-flag/bs.svg +1 -0
- package/DS1/x-flag/bt.svg +1 -0
- package/DS1/x-flag/bv.svg +1 -0
- package/DS1/x-flag/bw.svg +1 -0
- package/DS1/x-flag/by.svg +1 -0
- package/DS1/x-flag/bz.svg +1 -0
- package/DS1/x-flag/ca.svg +1 -0
- package/DS1/x-flag/cc.svg +1 -0
- package/DS1/x-flag/cd.svg +1 -0
- package/DS1/x-flag/cf.svg +1 -0
- package/DS1/x-flag/cg.svg +1 -0
- package/DS1/x-flag/ch.svg +1 -0
- package/DS1/x-flag/ci.svg +1 -0
- package/DS1/x-flag/ck.svg +1 -0
- package/DS1/x-flag/cl.svg +1 -0
- package/DS1/x-flag/cm.svg +1 -0
- package/DS1/x-flag/cn.svg +1 -0
- package/DS1/x-flag/co.svg +1 -0
- package/DS1/x-flag/cr.svg +1 -0
- package/DS1/x-flag/cu.svg +1 -0
- package/DS1/x-flag/cv.svg +1 -0
- package/DS1/x-flag/cw.svg +1 -0
- package/DS1/x-flag/cx.svg +1 -0
- package/DS1/x-flag/cy.svg +1 -0
- package/DS1/x-flag/cz.svg +1 -0
- package/DS1/x-flag/de.svg +1 -0
- package/DS1/x-flag/dj.svg +1 -0
- package/DS1/x-flag/dk.svg +1 -0
- package/DS1/x-flag/dm.svg +1 -0
- package/DS1/x-flag/do.svg +1 -0
- package/DS1/x-flag/dz.svg +1 -0
- package/DS1/x-flag/ec.svg +1 -0
- package/DS1/x-flag/ee.svg +1 -0
- package/DS1/x-flag/eg.svg +1 -0
- package/DS1/x-flag/eh.svg +1 -0
- package/DS1/x-flag/er.svg +1 -0
- package/DS1/x-flag/es-ct.svg +1 -0
- package/DS1/x-flag/es.svg +1 -0
- package/DS1/x-flag/et.svg +1 -0
- package/DS1/x-flag/eu.svg +1 -0
- package/DS1/x-flag/fi.svg +1 -0
- package/DS1/x-flag/fj.svg +1 -0
- package/DS1/x-flag/fk.svg +1 -0
- package/DS1/x-flag/fm.svg +1 -0
- package/DS1/x-flag/fo.svg +1 -0
- package/DS1/x-flag/fr.svg +1 -0
- package/DS1/x-flag/ga.svg +1 -0
- package/DS1/x-flag/gb-eng.svg +1 -0
- package/DS1/x-flag/gb-nir.svg +1 -0
- package/DS1/x-flag/gb-sct.svg +1 -0
- package/DS1/x-flag/gb-wls.svg +1 -0
- package/DS1/x-flag/gb.svg +1 -0
- package/DS1/x-flag/gd.svg +1 -0
- package/DS1/x-flag/ge.svg +1 -0
- package/DS1/x-flag/gf.svg +1 -0
- package/DS1/x-flag/gg.svg +1 -0
- package/DS1/x-flag/gh.svg +1 -0
- package/DS1/x-flag/gi.svg +1 -0
- package/DS1/x-flag/gl.svg +1 -0
- package/DS1/x-flag/gm.svg +1 -0
- package/DS1/x-flag/gn.svg +1 -0
- package/DS1/x-flag/gp.svg +1 -0
- package/DS1/x-flag/gq.svg +1 -0
- package/DS1/x-flag/gr.svg +1 -0
- package/DS1/x-flag/gs.svg +1 -0
- package/DS1/x-flag/gt.svg +1 -0
- package/DS1/x-flag/gu.svg +1 -0
- package/DS1/x-flag/gw.svg +1 -0
- package/DS1/x-flag/gy.svg +1 -0
- package/DS1/x-flag/hk.svg +1 -0
- package/DS1/x-flag/hm.svg +1 -0
- package/DS1/x-flag/hn.svg +1 -0
- package/DS1/x-flag/hr.svg +1 -0
- package/DS1/x-flag/ht.svg +1 -0
- package/DS1/x-flag/hu.svg +1 -0
- package/DS1/x-flag/ic.svg +1 -0
- package/DS1/x-flag/id.svg +1 -0
- package/DS1/x-flag/ie.svg +1 -0
- package/DS1/x-flag/il.svg +1 -0
- package/DS1/x-flag/im.svg +1 -0
- package/DS1/x-flag/in.svg +1 -0
- package/DS1/x-flag/io.svg +1 -0
- package/DS1/x-flag/iq.svg +1 -0
- package/DS1/x-flag/ir.svg +1 -0
- package/DS1/x-flag/is.svg +1 -0
- package/DS1/x-flag/it.svg +1 -0
- package/DS1/x-flag/je.svg +1 -0
- package/DS1/x-flag/jm.svg +1 -0
- package/DS1/x-flag/jo.svg +1 -0
- package/DS1/x-flag/jp.svg +1 -0
- package/DS1/x-flag/ke.svg +1 -0
- package/DS1/x-flag/kg.svg +1 -0
- package/DS1/x-flag/kh.svg +1 -0
- package/DS1/x-flag/ki.svg +1 -0
- package/DS1/x-flag/km.svg +1 -0
- package/DS1/x-flag/kn.svg +1 -0
- package/DS1/x-flag/kp.svg +1 -0
- package/DS1/x-flag/kr.svg +1 -0
- package/DS1/x-flag/kw.svg +1 -0
- package/DS1/x-flag/ky.svg +1 -0
- package/DS1/x-flag/kz.svg +1 -0
- package/DS1/x-flag/la.svg +1 -0
- package/DS1/x-flag/lb.svg +1 -0
- package/DS1/x-flag/lc.svg +1 -0
- package/DS1/x-flag/li.svg +1 -0
- package/DS1/x-flag/lk.svg +1 -0
- package/DS1/x-flag/lr.svg +1 -0
- package/DS1/x-flag/ls.svg +1 -0
- package/DS1/x-flag/lt.svg +1 -0
- package/DS1/x-flag/lu.svg +1 -0
- package/DS1/x-flag/lv.svg +1 -0
- package/DS1/x-flag/ly.svg +1 -0
- package/DS1/x-flag/ma.svg +1 -0
- package/DS1/x-flag/mc.svg +1 -0
- package/DS1/x-flag/md.svg +1 -0
- package/DS1/x-flag/me.svg +1 -0
- package/DS1/x-flag/mf.svg +1 -0
- package/DS1/x-flag/mg.svg +1 -0
- package/DS1/x-flag/mh.svg +1 -0
- package/DS1/x-flag/mk.svg +1 -0
- package/DS1/x-flag/ml.svg +1 -0
- package/DS1/x-flag/mm.svg +1 -0
- package/DS1/x-flag/mn.svg +1 -0
- package/DS1/x-flag/mo.svg +1 -0
- package/DS1/x-flag/mp.svg +1 -0
- package/DS1/x-flag/mq.svg +1 -0
- package/DS1/x-flag/mr.svg +1 -0
- package/DS1/x-flag/ms.svg +1 -0
- package/DS1/x-flag/mt.svg +1 -0
- package/DS1/x-flag/mu.svg +1 -0
- package/DS1/x-flag/mv.svg +1 -0
- package/DS1/x-flag/mw.svg +1 -0
- package/DS1/x-flag/mx.svg +1 -0
- package/DS1/x-flag/my.svg +1 -0
- package/DS1/x-flag/mz.svg +1 -0
- package/DS1/x-flag/na.svg +1 -0
- package/DS1/x-flag/nc.svg +1 -0
- package/DS1/x-flag/ne.svg +1 -0
- package/DS1/x-flag/nf.svg +1 -0
- package/DS1/x-flag/ng.svg +1 -0
- package/DS1/x-flag/ni.svg +1 -0
- package/DS1/x-flag/nl.svg +1 -0
- package/DS1/x-flag/no.svg +1 -0
- package/DS1/x-flag/np.svg +1 -0
- package/DS1/x-flag/nr.svg +1 -0
- package/DS1/x-flag/nu.svg +1 -0
- package/DS1/x-flag/nz.svg +1 -0
- package/DS1/x-flag/om.svg +1 -0
- package/DS1/x-flag/pa.svg +1 -0
- package/DS1/x-flag/pe.svg +1 -0
- package/DS1/x-flag/pf.svg +1 -0
- package/DS1/x-flag/pg.svg +1 -0
- package/DS1/x-flag/ph.svg +1 -0
- package/DS1/x-flag/pk.svg +1 -0
- package/DS1/x-flag/pl.svg +1 -0
- package/DS1/x-flag/pm.svg +1 -0
- package/DS1/x-flag/pn.svg +1 -0
- package/DS1/x-flag/pr.svg +1 -0
- package/DS1/x-flag/ps.svg +1 -0
- package/DS1/x-flag/pt.svg +1 -0
- package/DS1/x-flag/pw.svg +1 -0
- package/DS1/x-flag/py.svg +1 -0
- package/DS1/x-flag/qa.svg +1 -0
- package/DS1/x-flag/re.svg +1 -0
- package/DS1/x-flag/ro.svg +1 -0
- package/DS1/x-flag/rs.svg +1 -0
- package/DS1/x-flag/ru.svg +1 -0
- package/DS1/x-flag/rw.svg +1 -0
- package/DS1/x-flag/sa.svg +1 -0
- package/DS1/x-flag/sb.svg +1 -0
- package/DS1/x-flag/sc.svg +1 -0
- package/DS1/x-flag/sd.svg +1 -0
- package/DS1/x-flag/se.svg +1 -0
- package/DS1/x-flag/sg.svg +1 -0
- package/DS1/x-flag/sh.svg +1 -0
- package/DS1/x-flag/si.svg +1 -0
- package/DS1/x-flag/sj.svg +1 -0
- package/DS1/x-flag/sk.svg +1 -0
- package/DS1/x-flag/sl.svg +1 -0
- package/DS1/x-flag/sm.svg +1 -0
- package/DS1/x-flag/sn.svg +1 -0
- package/DS1/x-flag/so.svg +1 -0
- package/DS1/x-flag/sr.svg +1 -0
- package/DS1/x-flag/ss.svg +1 -0
- package/DS1/x-flag/st.svg +1 -0
- package/DS1/x-flag/sv.svg +1 -0
- package/DS1/x-flag/sx.svg +1 -0
- package/DS1/x-flag/sy.svg +1 -0
- package/DS1/x-flag/sz.svg +1 -0
- package/DS1/x-flag/ta.svg +1 -0
- package/DS1/x-flag/tc.svg +1 -0
- package/DS1/x-flag/td.svg +1 -0
- package/DS1/x-flag/tf.svg +1 -0
- package/DS1/x-flag/tg.svg +1 -0
- package/DS1/x-flag/th.svg +1 -0
- package/DS1/x-flag/tj.svg +1 -0
- package/DS1/x-flag/tk.svg +1 -0
- package/DS1/x-flag/tl.svg +1 -0
- package/DS1/x-flag/tm.svg +1 -0
- package/DS1/x-flag/tn.svg +1 -0
- package/DS1/x-flag/to.svg +1 -0
- package/DS1/x-flag/tr.svg +1 -0
- package/DS1/x-flag/tt.svg +1 -0
- package/DS1/x-flag/tv.svg +1 -0
- package/DS1/x-flag/tw.svg +1 -0
- package/DS1/x-flag/tz.svg +1 -0
- package/DS1/x-flag/ua.svg +1 -0
- package/DS1/x-flag/ug.svg +1 -0
- package/DS1/x-flag/um.svg +1 -0
- package/DS1/x-flag/us.svg +1 -0
- package/DS1/x-flag/uy.svg +1 -0
- package/DS1/x-flag/uz.svg +1 -0
- package/DS1/x-flag/va.svg +1 -0
- package/DS1/x-flag/vc.svg +1 -0
- package/DS1/x-flag/ve.svg +1 -0
- package/DS1/x-flag/vg.svg +1 -0
- package/DS1/x-flag/vi.svg +1 -0
- package/DS1/x-flag/vn.svg +1 -0
- package/DS1/x-flag/vu.svg +1 -0
- package/DS1/x-flag/wf.svg +1 -0
- package/DS1/x-flag/ws.svg +1 -0
- package/DS1/x-flag/xk.svg +1 -0
- package/DS1/x-flag/ye.svg +1 -0
- package/DS1/x-flag/yt.svg +1 -0
- package/DS1/x-flag/za.svg +1 -0
- package/DS1/x-flag/zm.svg +1 -0
- package/DS1/x-flag/zw.svg +1 -0
- package/DS1/x-icon/android.svg +9 -0
- package/DS1/x-icon/arrange-check.svg +1 -1
- package/DS1/x-icon/arrange-page.svg +1 -1
- package/DS1/x-icon/chrome.svg +7 -0
- package/DS1/x-icon/edge-brand.svg +6 -0
- package/DS1/x-icon/firefox.svg +4 -0
- package/DS1/x-icon/health.svg +3 -0
- package/DS1/x-icon/left.svg +2 -3
- package/DS1/x-icon/linux.svg +1 -0
- package/DS1/x-icon/material.svg +1 -0
- package/DS1/x-icon/milestone-planned.svg +3 -0
- package/DS1/x-icon/milestone.svg +3 -0
- package/DS1/x-icon/more.svg +1 -5
- package/DS1/x-icon/network.svg +3 -0
- package/DS1/x-icon/opera.svg +4 -0
- package/DS1/x-icon/prediction.svg +3 -0
- package/DS1/x-icon/priority-high.svg +3 -0
- package/DS1/x-icon/priority-low.svg +3 -0
- package/DS1/x-icon/priority-medium.svg +3 -0
- package/DS1/x-icon/project.svg +3 -0
- package/DS1/x-icon/redo.svg +2 -2
- package/DS1/x-icon/repeat-small.svg +3 -0
- package/DS1/x-icon/safari.svg +4 -0
- package/DS1/x-icon/scaling.svg +3 -0
- package/DS1/x-icon/tabs.svg +4 -0
- package/DS1/x-icon/windows.svg +5 -0
- package/README.md +3 -3
- package/dist/0-face/button-label.d.ts +5 -0
- package/dist/0-face/button-label.d.ts.map +1 -0
- package/dist/0-face/button-label.js +31 -0
- package/dist/0-face/calendar.d.ts +1 -1
- package/dist/0-face/calendar.d.ts.map +1 -1
- package/dist/0-face/calendar.js +6 -3
- package/dist/0-face/carousel.d.ts +9 -0
- package/dist/0-face/carousel.d.ts.map +1 -1
- package/dist/0-face/carousel.js +80 -0
- package/dist/0-face/constraint-panel.d.ts +43 -0
- package/dist/0-face/constraint-panel.d.ts.map +1 -0
- package/dist/0-face/constraint-panel.js +451 -0
- package/dist/0-face/constraint-site.d.ts +73 -0
- package/dist/0-face/constraint-site.d.ts.map +1 -0
- package/dist/0-face/constraint-site.js +186 -0
- package/dist/0-face/constraints.d.ts +47 -0
- package/dist/0-face/constraints.d.ts.map +1 -0
- package/dist/0-face/constraints.js +274 -0
- package/dist/0-face/copy-text.js +1 -1
- package/dist/0-face/cut-fade.d.ts +112 -0
- package/dist/0-face/cut-fade.d.ts.map +1 -0
- package/dist/0-face/cut-fade.js +274 -0
- package/dist/0-face/define.d.ts.map +1 -1
- package/dist/0-face/define.js +24 -3
- package/dist/0-face/face-correction.d.ts +51 -0
- package/dist/0-face/face-correction.d.ts.map +1 -0
- package/dist/0-face/face-correction.js +80 -0
- package/dist/0-face/failures.d.ts +16 -0
- package/dist/0-face/failures.d.ts.map +1 -0
- package/dist/0-face/failures.js +21 -0
- package/dist/0-face/fault-lines.d.ts +20 -0
- package/dist/0-face/fault-lines.d.ts.map +1 -0
- package/dist/0-face/fault-lines.js +172 -0
- package/dist/0-face/feedback-overlays.d.ts.map +1 -1
- package/dist/0-face/feedback-overlays.js +5 -3
- package/dist/0-face/feedback.d.ts +2 -0
- package/dist/0-face/feedback.d.ts.map +1 -1
- package/dist/0-face/feedback.js +1 -0
- package/dist/0-face/focus.d.ts +71 -0
- package/dist/0-face/focus.d.ts.map +1 -0
- package/dist/0-face/focus.js +196 -0
- package/dist/0-face/grid.d.ts +11 -1
- package/dist/0-face/grid.d.ts.map +1 -1
- package/dist/0-face/grid.js +90 -4
- package/dist/0-face/i18n.d.ts.map +1 -1
- package/dist/0-face/i18n.js +38 -16
- package/dist/0-face/layout-inspector.d.ts +5 -1
- package/dist/0-face/layout-inspector.d.ts.map +1 -1
- package/dist/0-face/layout-inspector.js +130 -104
- package/dist/0-face/preference-options.d.ts +4 -0
- package/dist/0-face/preference-options.d.ts.map +1 -1
- package/dist/0-face/scaling.d.ts +8 -1
- package/dist/0-face/scaling.d.ts.map +1 -1
- package/dist/0-face/scaling.js +19 -2
- package/dist/0-face/shape.d.ts +35 -0
- package/dist/0-face/shape.d.ts.map +1 -0
- package/dist/0-face/shape.js +23 -0
- package/dist/0-face/text-contrast.d.ts +22 -0
- package/dist/0-face/text-contrast.d.ts.map +1 -0
- package/dist/0-face/text-contrast.js +178 -0
- package/dist/0-face/theme-bootstrap.js +56 -0
- package/dist/0-face/toast-queue.d.ts +4 -0
- package/dist/0-face/toast-queue.d.ts.map +1 -1
- package/dist/0-face/toast-queue.js +4 -1
- package/dist/0-face/translations/da.json +10 -1
- package/dist/0-face/translations/de.json +10 -1
- package/dist/0-face/translations/en.json +10 -1
- package/dist/0-face/translations/es.json +10 -1
- package/dist/0-face/translations/fi.json +10 -1
- package/dist/0-face/translations/fr.json +10 -1
- package/dist/0-face/translations/is.json +10 -1
- package/dist/0-face/translations/it.json +10 -1
- package/dist/0-face/translations/ja.json +10 -1
- package/dist/0-face/translations/ko.json +10 -1
- package/dist/0-face/translations/nb.json +10 -1
- package/dist/0-face/translations/pt-BR.json +10 -1
- package/dist/0-face/translations/pt.json +10 -1
- package/dist/0-face/translations/sv.json +10 -1
- package/dist/0-face/translations/zh.json +10 -1
- package/dist/0-face/word-wrap.d.ts +6 -0
- package/dist/0-face/word-wrap.d.ts.map +1 -0
- package/dist/0-face/word-wrap.js +119 -0
- package/dist/1-root/foundation.d.ts +12 -0
- package/dist/1-root/foundation.d.ts.map +1 -0
- package/dist/1-root/foundation.js +59 -0
- package/dist/2-core/button-split.d.ts +1 -0
- package/dist/2-core/button-split.d.ts.map +1 -1
- package/dist/2-core/button-split.js +9 -4
- package/dist/2-core/chart/grid-chart.d.ts +2 -0
- package/dist/2-core/chart/grid-chart.d.ts.map +1 -1
- package/dist/2-core/chart/grid-chart.js +2 -1
- package/dist/2-core/ds-activity.d.ts +25 -0
- package/dist/2-core/ds-activity.d.ts.map +1 -0
- package/dist/2-core/ds-activity.js +28 -0
- package/dist/2-core/ds-app-chrome.d.ts +2 -0
- package/dist/2-core/ds-app-chrome.d.ts.map +1 -0
- package/dist/2-core/ds-app-chrome.js +1 -0
- package/dist/2-core/ds-avatar.d.ts +12 -0
- package/dist/2-core/ds-avatar.d.ts.map +1 -1
- package/dist/2-core/ds-avatar.js +86 -7
- package/dist/2-core/ds-badge.d.ts +7 -1
- package/dist/2-core/ds-badge.d.ts.map +1 -1
- package/dist/2-core/ds-badge.js +8 -1
- package/dist/2-core/ds-button.d.ts +12 -0
- package/dist/2-core/ds-button.d.ts.map +1 -1
- package/dist/2-core/ds-button.js +4 -0
- package/dist/2-core/ds-chart.d.ts +28 -3
- package/dist/2-core/ds-chart.d.ts.map +1 -1
- package/dist/2-core/ds-chart.js +110 -130
- package/dist/2-core/ds-cycle.d.ts +2 -2
- package/dist/2-core/ds-cycle.d.ts.map +1 -1
- package/dist/2-core/ds-cycle.js +36 -171
- package/dist/2-core/ds-device-bezel.d.ts +2 -0
- package/dist/2-core/ds-device-bezel.d.ts.map +1 -0
- package/dist/2-core/ds-device-bezel.js +1 -0
- package/dist/2-core/ds-empty.d.ts +2 -2
- package/dist/2-core/ds-empty.d.ts.map +1 -1
- package/dist/2-core/ds-empty.js +6 -4
- package/dist/2-core/ds-input.d.ts +7 -0
- package/dist/2-core/ds-input.d.ts.map +1 -1
- package/dist/2-core/ds-input.js +23 -3
- package/dist/2-core/ds-message.d.ts +11 -0
- package/dist/2-core/ds-message.d.ts.map +1 -1
- package/dist/2-core/ds-message.js +18 -0
- package/dist/2-core/ds-number.d.ts +86 -0
- package/dist/2-core/ds-number.d.ts.map +1 -0
- package/dist/2-core/ds-number.js +326 -0
- package/dist/2-core/ds-progress.d.ts +3 -0
- package/dist/2-core/ds-progress.d.ts.map +1 -1
- package/dist/2-core/ds-progress.js +3 -0
- package/dist/2-core/ds-radio.d.ts +3 -0
- package/dist/2-core/ds-radio.d.ts.map +1 -1
- package/dist/2-core/ds-radio.js +10 -1
- package/dist/2-core/ds-stat.d.ts +3 -0
- package/dist/2-core/ds-stat.d.ts.map +1 -1
- package/dist/2-core/ds-stat.js +3 -0
- package/dist/2-core/ds-switch.d.ts +14 -1
- package/dist/2-core/ds-switch.d.ts.map +1 -1
- package/dist/2-core/ds-switch.js +3 -1
- package/dist/2-core/ds-tag.d.ts +8 -1
- package/dist/2-core/ds-tag.d.ts.map +1 -1
- package/dist/2-core/ds-tag.js +12 -2
- package/dist/2-core/ds-text.d.ts +7 -0
- package/dist/2-core/ds-text.d.ts.map +1 -1
- package/dist/2-core/ds-text.js +7 -0
- package/dist/2-core/ds-token.d.ts +3 -0
- package/dist/2-core/ds-token.d.ts.map +1 -1
- package/dist/2-core/ds-token.js +3 -0
- package/dist/2-core/ds-tooltip.d.ts.map +1 -1
- package/dist/2-core/ds-tooltip.js +23 -2
- package/dist/2-core/ds-window.d.ts +2 -0
- package/dist/2-core/ds-window.d.ts.map +1 -0
- package/dist/2-core/ds-window.js +1 -0
- package/dist/2-core/generated/ds-icon-map.d.ts +27 -6
- package/dist/2-core/generated/ds-icon-map.d.ts.map +1 -1
- package/dist/2-core/generated/ds-icon-map.js +47 -5
- package/dist/2-core/generated/icons-extra.d.ts.map +1 -1
- package/dist/2-core/generated/icons-extra.js +24 -3
- package/dist/2-core/generated/icons-immediate.d.ts +1 -1
- package/dist/2-core/generated/icons-immediate.d.ts.map +1 -1
- package/dist/2-core/generated/icons-immediate.js +3 -3
- package/dist/2-core/number-expression.d.ts +12 -0
- package/dist/2-core/number-expression.d.ts.map +1 -0
- package/dist/2-core/number-expression.js +82 -0
- package/dist/3-unit/calendar-base.d.ts +5 -0
- package/dist/3-unit/calendar-base.d.ts.map +1 -1
- package/dist/3-unit/calendar-base.js +3 -2
- package/dist/3-unit/context-trigger.d.ts.map +1 -1
- package/dist/3-unit/context-trigger.js +32 -2
- package/dist/3-unit/ds-calendar.d.ts +197 -4
- package/dist/3-unit/ds-calendar.d.ts.map +1 -1
- package/dist/3-unit/ds-calendar.js +687 -45
- package/dist/3-unit/ds-carousel.d.ts +3 -0
- package/dist/3-unit/ds-carousel.d.ts.map +1 -1
- package/dist/3-unit/ds-carousel.js +13 -1
- package/dist/3-unit/ds-context.d.ts +18 -1
- package/dist/3-unit/ds-context.d.ts.map +1 -1
- package/dist/3-unit/ds-context.js +37 -1
- package/dist/3-unit/ds-form.d.ts +9 -0
- package/dist/3-unit/ds-form.d.ts.map +1 -1
- package/dist/3-unit/ds-form.js +52 -11
- package/dist/3-unit/ds-list.d.ts +20 -1
- package/dist/3-unit/ds-list.d.ts.map +1 -1
- package/dist/3-unit/ds-list.js +31 -3
- package/dist/3-unit/ds-matrix.d.ts +3 -0
- package/dist/3-unit/ds-matrix.d.ts.map +1 -1
- package/dist/3-unit/ds-matrix.js +3 -0
- package/dist/3-unit/ds-menu.d.ts.map +1 -1
- package/dist/3-unit/ds-menu.js +17 -1
- package/dist/3-unit/ds-row.d.ts +13 -0
- package/dist/3-unit/ds-row.d.ts.map +1 -1
- package/dist/3-unit/ds-row.js +15 -2
- package/dist/3-unit/ds-select.js +1 -1
- package/dist/3-unit/ds-sidebar.d.ts +17 -0
- package/dist/3-unit/ds-sidebar.d.ts.map +1 -1
- package/dist/3-unit/ds-sidebar.js +14 -3
- package/dist/3-unit/ds-table-of-contents.d.ts +68 -0
- package/dist/3-unit/ds-table-of-contents.d.ts.map +1 -0
- package/dist/3-unit/ds-table-of-contents.js +190 -0
- package/dist/3-unit/ds-table.d.ts.map +1 -1
- package/dist/3-unit/ds-table.js +2 -1
- package/dist/3-unit/ds-tabs.d.ts +32 -1
- package/dist/3-unit/ds-tabs.d.ts.map +1 -1
- package/dist/3-unit/ds-tabs.js +39 -1
- package/dist/4-page/ds-container.d.ts.map +1 -1
- package/dist/4-page/ds-container.js +2 -1
- package/dist/4-page/ds-document.d.ts +3 -0
- package/dist/4-page/ds-document.d.ts.map +1 -1
- package/dist/4-page/ds-document.js +3 -0
- package/dist/4-page/ds-footer.d.ts +47 -7
- package/dist/4-page/ds-footer.d.ts.map +1 -1
- package/dist/4-page/ds-footer.js +58 -14
- package/dist/4-page/ds-header.d.ts +6 -0
- package/dist/4-page/ds-header.d.ts.map +1 -1
- package/dist/4-page/ds-header.js +4 -1
- package/dist/4-page/ds-overlay.d.ts +2 -2
- package/dist/4-page/ds-overlay.d.ts.map +1 -1
- package/dist/4-page/ds-overlay.js +8 -3
- package/dist/4-page/ds-page.d.ts +12 -0
- package/dist/4-page/ds-page.d.ts.map +1 -1
- package/dist/4-page/ds-page.js +19 -1
- package/dist/4-page/email.d.ts +2 -0
- package/dist/4-page/email.d.ts.map +1 -1
- package/dist/4-page/email.js +5 -4
- package/dist/5-visual-kit/app-chrome.d.ts +98 -0
- package/dist/5-visual-kit/app-chrome.d.ts.map +1 -0
- package/dist/5-visual-kit/app-chrome.js +119 -0
- package/dist/5-visual-kit/bezels.d.ts +30 -0
- package/dist/5-visual-kit/bezels.d.ts.map +1 -0
- package/dist/5-visual-kit/bezels.js +388 -0
- package/dist/5-visual-kit/browser-icons.d.ts +5 -0
- package/dist/5-visual-kit/browser-icons.d.ts.map +1 -0
- package/dist/5-visual-kit/browser-icons.js +51 -0
- package/dist/5-visual-kit/browser-styles.d.ts +3 -0
- package/dist/5-visual-kit/browser-styles.d.ts.map +1 -0
- package/dist/5-visual-kit/browser-styles.js +52 -0
- package/dist/5-visual-kit/device-bezel.d.ts +84 -0
- package/dist/5-visual-kit/device-bezel.d.ts.map +1 -0
- package/dist/5-visual-kit/device-bezel.js +56 -0
- package/dist/5-visual-kit/instagram-frame.d.ts +8 -0
- package/dist/5-visual-kit/instagram-frame.d.ts.map +1 -0
- package/dist/5-visual-kit/instagram-frame.js +13 -0
- package/dist/5-visual-kit/instagram.d.ts +18 -0
- package/dist/5-visual-kit/instagram.d.ts.map +1 -0
- package/dist/5-visual-kit/instagram.js +39 -0
- package/dist/5-visual-kit/mobile-chrome.d.ts +4 -0
- package/dist/5-visual-kit/mobile-chrome.d.ts.map +1 -0
- package/dist/5-visual-kit/mobile-chrome.js +24 -0
- package/dist/5-visual-kit/native-icon-paths.d.ts +146 -0
- package/dist/5-visual-kit/native-icon-paths.d.ts.map +1 -0
- package/dist/5-visual-kit/native-icon-paths.js +145 -0
- package/dist/5-visual-kit/window.d.ts +51 -0
- package/dist/5-visual-kit/window.d.ts.map +1 -0
- package/dist/5-visual-kit/window.js +20 -0
- package/dist/browser-icons-BFbS1tIx.js +56 -0
- package/dist/browser-icons-BFbS1tIx.js.map +1 -0
- package/dist/browser-icons-CT8qU77Z.js +48 -0
- package/dist/browser-icons-CT8qU77Z.js.map +1 -0
- package/dist/components.d.ts +1 -0
- package/dist/components.d.ts.map +1 -1
- package/dist/components.js +1 -0
- package/dist/design-system-1.bundle.js +5874 -2097
- package/dist/design-system-1.bundle.js.map +1 -1
- package/dist/design-system-1.bundle.min.js +7332 -4682
- package/dist/design-system-1.bundle.min.js.map +1 -1
- package/dist/design-system-1.css +1 -1
- package/dist/feedback.js +197 -167
- package/dist/feedback.js.map +1 -1
- package/dist/{icons-extra-CRxxWFjX.js → icons-extra-BMPvmQPI.js} +25 -4
- package/dist/icons-extra-BMPvmQPI.js.map +1 -0
- package/dist/{icons-extra-H5pqCM_R.js → icons-extra-BfYOw-cR.js} +83 -5
- package/dist/icons-extra-BfYOw-cR.js.map +1 -0
- package/dist/index.d.ts +12 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/instagram-CKofs55A.js +50 -0
- package/dist/instagram-CKofs55A.js.map +1 -0
- package/dist/instagram-CcYf5oiM.js +48 -0
- package/dist/instagram-CcYf5oiM.js.map +1 -0
- package/dist/lit-Ce_uWYM4.js +612 -0
- package/dist/lit-Ce_uWYM4.js.map +1 -0
- package/dist/lit-DGUVm1Ce.js +557 -0
- package/dist/lit-DGUVm1Ce.js.map +1 -0
- package/dist/native-icon-paths-CB9evXBM.js +93 -0
- package/dist/native-icon-paths-CB9evXBM.js.map +1 -0
- package/dist/native-icon-paths-zay-3g_n.js +150 -0
- package/dist/native-icon-paths-zay-3g_n.js.map +1 -0
- package/dist/react/avatar.d.ts +1 -1
- package/dist/react/choice.d.ts +4 -0
- package/dist/react/choice.d.ts.map +1 -1
- package/dist/react/choice.js +2 -2
- package/dist/react/context.d.ts +19 -4
- package/dist/react/context.d.ts.map +1 -1
- package/dist/react/context.js +4 -4
- package/dist/react/empty.d.ts +2 -2
- package/dist/react/empty.d.ts.map +1 -1
- package/dist/react/empty.js +1 -1
- package/dist/react/footer.d.ts +5 -5
- package/dist/react/footer.d.ts.map +1 -1
- package/dist/react/footer.js +2 -2
- package/dist/react/header.d.ts +2 -2
- package/dist/react/header.d.ts.map +1 -1
- package/dist/react/message.d.ts +4 -0
- package/dist/react/message.d.ts.map +1 -1
- package/dist/react/message.js +2 -2
- package/dist/react/number.d.ts +53 -0
- package/dist/react/number.d.ts.map +1 -0
- package/dist/react/number.js +25 -0
- package/dist/react/page.d.ts +1 -1
- package/dist/react/sidebar.d.ts +4 -0
- package/dist/react/sidebar.d.ts.map +1 -1
- package/dist/react/sidebar.js +2 -2
- package/dist/react/table-of-contents.d.ts +19 -0
- package/dist/react/table-of-contents.d.ts.map +1 -0
- package/dist/react/table-of-contents.js +28 -0
- package/dist/{shared-words-CdMDVtXx.js → shared-words-C5cjxMjZ.js} +151 -16
- package/dist/shared-words-C5cjxMjZ.js.map +1 -0
- package/dist/{shared-words-BtRee8xR.js → shared-words-Cdm7xd-_.js} +163 -28
- package/dist/shared-words-Cdm7xd-_.js.map +1 -0
- package/package.json +25 -12
- package/DS1/3-unit/ds-calendar-large.ts +0 -353
- package/DS1/3-unit/styles/ds-calendar-large.css +0 -178
- package/dist/3-unit/ds-calendar-large.d.ts +0 -142
- package/dist/3-unit/ds-calendar-large.d.ts.map +0 -1
- package/dist/3-unit/ds-calendar-large.js +0 -308
- package/dist/icons-extra-CRxxWFjX.js.map +0 -1
- package/dist/icons-extra-H5pqCM_R.js.map +0 -1
- package/dist/shared-words-BtRee8xR.js.map +0 -1
- package/dist/shared-words-CdMDVtXx.js.map +0 -1
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// The constraints on every page of a site, at every phone width, from inside the browser: the constraint
|
|
2
|
+
// panel's Site check (constraint-panel.ts). Each page in the site's sitemap is opened once in a frame of
|
|
3
|
+
// the site's own, out of sight, then made as wide as each phone in turn and drawn at that phone's scale,
|
|
4
|
+
// and measured with every rule in constraints.ts at each width. Three pages are checked at a time.
|
|
5
|
+
// scripts/constraints-phone.ts does the same from a terminal, in WebKit as an iPhone.
|
|
6
|
+
import { constraints, measureConstraints, type Constraint } from './constraints';
|
|
7
|
+
import { calculateScalingFactor, defaultScalingConfig } from './scaling';
|
|
8
|
+
|
|
9
|
+
/** The narrowest phone still sold, the common ones and the widest. */
|
|
10
|
+
export const PHONE_WIDTHS = [320, 375, 393, 430];
|
|
11
|
+
export const VIEWPORT_WIDTHS = [...PHONE_WIDTHS, 768, 1024, 1440];
|
|
12
|
+
/** Pages checked side by side, each in its own frame. */
|
|
13
|
+
export const PAGES_AT_ONCE = 3;
|
|
14
|
+
|
|
15
|
+
/** A rule broken on a page at a width, said in words, since the element stays in its frame. */
|
|
16
|
+
export type SiteFinding = { code: string; rule: string; message: string; element: string; review?: boolean };
|
|
17
|
+
/** What one page at one width kept and broke: each rule's count of what it measured, and its findings. */
|
|
18
|
+
export type PageCheck = { findings: SiteFinding[]; measured: Record<string, number> };
|
|
19
|
+
export type PageResult = PageCheck & { url: string; width: number; error?: string };
|
|
20
|
+
|
|
21
|
+
/** An element as a person finds it: its tag, id and classes, and the start of its words. */
|
|
22
|
+
export function describeElement(element: Element): string {
|
|
23
|
+
const name = element.localName + (element.id ? `#${element.id}` : '') + Array.from(element.classList, c => `.${c}`).join('');
|
|
24
|
+
const words = (element.textContent ?? '').trim().replace(/\s+/g, ' ').slice(0, 40);
|
|
25
|
+
return words ? `${name} "${words}"` : name;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The pages a sitemap lists on the site's own origin, in its order and once each; none when it lists none. */
|
|
29
|
+
export function sitemapPages(xml: string, origin: string): string[] {
|
|
30
|
+
const pages = [...xml.matchAll(/<loc>\s*([^<\s]+)\s*<\/loc>/g)].flatMap(([, loc]) => {
|
|
31
|
+
try {
|
|
32
|
+
const url = new URL(loc!.replace(/&/g, '&'), origin);
|
|
33
|
+
return url.origin === origin ? [url.href] : [];
|
|
34
|
+
} catch {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return [...new Set(pages)];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The site's pages: its sitemap's, or else the page itself and every page it links to on the site. */
|
|
42
|
+
export async function findSitePages(page: Document = document): Promise<string[]> {
|
|
43
|
+
const { origin } = page.location ?? window.location;
|
|
44
|
+
try {
|
|
45
|
+
const answer = await fetch(new URL('/sitemap.xml', origin));
|
|
46
|
+
if (answer.ok) {
|
|
47
|
+
const pages = sitemapPages(await answer.text(), origin);
|
|
48
|
+
if (pages.length) return pages;
|
|
49
|
+
}
|
|
50
|
+
} catch { /* No sitemap: the links below. */ }
|
|
51
|
+
const links = Array.from(page.querySelectorAll<HTMLAnchorElement>('a[href]'), link => new URL(link.href, origin))
|
|
52
|
+
.filter(url => url.origin === origin && !/\.\w+$/.test(url.pathname))
|
|
53
|
+
.map(url => `${url.origin}${url.pathname}`);
|
|
54
|
+
return [...new Set([`${origin}${(page.location ?? window.location).pathname}`, ...links])];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const wait = (ms: number) => new Promise(done => setTimeout(done, ms));
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Opens a page once in a frame inside `host`, then makes the frame as wide as each width in turn, draws
|
|
61
|
+
* the page at that width's phone scale, and returns what every rule measured and found at each, in the
|
|
62
|
+
* order of `widths`. The page reflows at each width without loading again. The address carries `ph=out`,
|
|
63
|
+
* the analytics opt-out most trackers honour, so the check counts no visits.
|
|
64
|
+
*/
|
|
65
|
+
export async function inspectInFrame(host: Element | ShadowRoot, url: string, widths: number[] = VIEWPORT_WIDTHS, height = 844): Promise<PageCheck[]> {
|
|
66
|
+
const frame = document.createElement('iframe');
|
|
67
|
+
frame.setAttribute('aria-hidden', 'true');
|
|
68
|
+
frame.tabIndex = -1;
|
|
69
|
+
frame.style.cssText = `position:absolute;top:0;left:-1000px;width:${widths[0]}px;height:${height}px;border:0;visibility:hidden;pointer-events:none`;
|
|
70
|
+
const address = new URL(url);
|
|
71
|
+
address.searchParams.set('ph', 'out');
|
|
72
|
+
try {
|
|
73
|
+
const loaded = new Promise<void>((done, fail) => {
|
|
74
|
+
frame.addEventListener('load', () => done(), { once: true });
|
|
75
|
+
setTimeout(() => fail(new Error('did not load in 30 seconds')), 30_000);
|
|
76
|
+
});
|
|
77
|
+
frame.src = address.href;
|
|
78
|
+
host.appendChild(frame);
|
|
79
|
+
await loaded;
|
|
80
|
+
const page = frame.contentDocument;
|
|
81
|
+
if (!page?.documentElement) throw new Error('could not be read');
|
|
82
|
+
await page.fonts?.ready;
|
|
83
|
+
const root = page.documentElement;
|
|
84
|
+
const checks: PageCheck[] = [];
|
|
85
|
+
for (const width of widths) {
|
|
86
|
+
frame.style.width = `${width}px`;
|
|
87
|
+
// The phone's scale for this width, whichever browser draws the frame: a computer's would draw it
|
|
88
|
+
// at 1, and a page that scales once on load would keep the first width's.
|
|
89
|
+
root.style.setProperty('--sf', String(calculateScalingFactor(width, { ...defaultScalingConfig, snap: 'layout' })));
|
|
90
|
+
root.setAttribute('data-mobile', '');
|
|
91
|
+
void root.scrollWidth;
|
|
92
|
+
// Room for what follows a resize on its own: resize observers, a carousel measuring its slides.
|
|
93
|
+
await wait(80);
|
|
94
|
+
checks.push(checkPage(page));
|
|
95
|
+
}
|
|
96
|
+
return checks;
|
|
97
|
+
} finally {
|
|
98
|
+
frame.remove();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Every rule on a page: what it measured, and each place it is broken, said in words. */
|
|
103
|
+
export function checkPage(page: ParentNode): PageCheck {
|
|
104
|
+
const measured: Record<string, number> = {};
|
|
105
|
+
const findings: SiteFinding[] = [];
|
|
106
|
+
for (const { constraint, measured: count, findings: found } of measureConstraints(page)) {
|
|
107
|
+
measured[constraint.code] = count;
|
|
108
|
+
for (const { message, element, review } of found) findings.push({ code: constraint.code, rule: constraint.rule, message, element: describeElement(element), ...(review ? { review } : {}) });
|
|
109
|
+
}
|
|
110
|
+
return { findings, measured };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Checks every page at every width, `PAGES_AT_ONCE` pages side by side, and reports each page's result at
|
|
115
|
+
* each width as it comes. Stops starting pages when `signal` aborts. Whatever the pages store in this
|
|
116
|
+
* browser while they are checked is put back afterwards.
|
|
117
|
+
*/
|
|
118
|
+
export async function checkSite({ pages, widths = VIEWPORT_WIDTHS, inspect, onResult, signal, atOnce = PAGES_AT_ONCE }: {
|
|
119
|
+
pages: string[];
|
|
120
|
+
widths?: number[];
|
|
121
|
+
/** One page at every width, in the order of `widths`. */
|
|
122
|
+
inspect: (url: string, widths: number[]) => Promise<PageCheck[]>;
|
|
123
|
+
onResult: (result: PageResult, done: number, total: number) => void;
|
|
124
|
+
signal?: AbortSignal;
|
|
125
|
+
atOnce?: number;
|
|
126
|
+
}): Promise<PageResult[]> {
|
|
127
|
+
const kept = storedNow();
|
|
128
|
+
const results: PageResult[] = [];
|
|
129
|
+
const total = pages.length * widths.length;
|
|
130
|
+
const queue = [...pages];
|
|
131
|
+
const next = async (): Promise<void> => {
|
|
132
|
+
for (let url = queue.shift(); url && !signal?.aborted; url = queue.shift()) {
|
|
133
|
+
const checks = await inspect(url, widths).catch((error: Error) => error);
|
|
134
|
+
for (const [index, width] of widths.entries()) {
|
|
135
|
+
const result: PageResult = checks instanceof Error
|
|
136
|
+
? { url, width, findings: [], measured: {}, error: checks.message }
|
|
137
|
+
: { url, width, ...(checks[index] ?? { findings: [], measured: {} }) };
|
|
138
|
+
results.push(result);
|
|
139
|
+
if (!signal?.aborted) onResult(result, results.length, total);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
try {
|
|
144
|
+
await Promise.all(Array.from({ length: Math.max(1, atOnce) }, next));
|
|
145
|
+
} finally {
|
|
146
|
+
putBack(kept);
|
|
147
|
+
}
|
|
148
|
+
return results;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** This site's storage in this browser, to put back after the check. */
|
|
152
|
+
function storedNow(): Map<string, string> | null {
|
|
153
|
+
try {
|
|
154
|
+
return new Map(Object.keys(localStorage).map(key => [key, localStorage.getItem(key)!]));
|
|
155
|
+
} catch {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function putBack(kept: Map<string, string> | null): void {
|
|
161
|
+
if (!kept) return;
|
|
162
|
+
try {
|
|
163
|
+
for (const key of Object.keys(localStorage)) if (!kept.has(key)) localStorage.removeItem(key);
|
|
164
|
+
for (const [key, value] of kept) if (localStorage.getItem(key) !== value) localStorage.setItem(key, value);
|
|
165
|
+
} catch { /* Storage blocked: nothing was kept either. */ }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** A rule over a check: how many things it measured, on how many of the page views, and how often it broke. */
|
|
169
|
+
export type RuleSummary = { constraint: Constraint; measured: number; views: number; broken: number };
|
|
170
|
+
|
|
171
|
+
export function summarizeRules(results: PageCheck[]): RuleSummary[] {
|
|
172
|
+
return constraints.map(constraint => {
|
|
173
|
+
const counts = results.map(result => result.measured[constraint.code] ?? 0);
|
|
174
|
+
return {
|
|
175
|
+
constraint,
|
|
176
|
+
measured: counts.reduce((sum, count) => sum + count, 0),
|
|
177
|
+
views: counts.filter(count => count > 0).length,
|
|
178
|
+
broken: results.reduce((sum, result) => sum + result.findings.filter(finding => finding.code === constraint.code && !finding.review).length, 0),
|
|
179
|
+
};
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const count = (n: number, [one, many]: [string, string]) => `${n.toLocaleString('en')} ${n === 1 ? one : many}`;
|
|
184
|
+
|
|
185
|
+
/** What a rule did, in a sentence: kept, broken, or nothing on the pages for it to measure. `views` is how many page views were checked; one is this page. */
|
|
186
|
+
export function ruleLine({ constraint, measured, views, broken }: RuleSummary, total: number): string {
|
|
187
|
+
if (!measured) return `Nothing to measure: no ${constraint.measures[1]} ${total === 1 ? 'on this page' : 'on these pages'}.`;
|
|
188
|
+
const where = total === 1 ? 'on this page' : `on ${views.toLocaleString('en')} of ${total.toLocaleString('en')} page views`;
|
|
189
|
+
return broken
|
|
190
|
+
? `Broken ${broken === 1 ? 'once' : `${broken.toLocaleString('en')} times`}, among ${count(measured, constraint.measures)} measured ${where}.`
|
|
191
|
+
: `Kept by ${count(measured, constraint.measures)} measured ${where}.`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Every page checked, in the order checked, with how much it broke at each width. */
|
|
195
|
+
export function summarizePages(results: PageResult[]): { url: string; widths: { width: number; broken: number; error?: string }[] }[] {
|
|
196
|
+
const pages = new Map<string, { width: number; broken: number; error?: string }[]>();
|
|
197
|
+
for (const { url, width, findings, error } of results) {
|
|
198
|
+
pages.set(url, [...(pages.get(url) ?? []), { width, broken: findings.filter(finding => !finding.review).length, error }].sort((a, b) => a.width - b.width));
|
|
199
|
+
}
|
|
200
|
+
return [...pages].map(([url, widths]) => ({ url, widths }));
|
|
201
|
+
}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { faultLineGrids, inspectFaultLines } from './fault-lines';
|
|
2
|
+
import { labelledButtons, inspectButtonLabels } from './button-label';
|
|
3
|
+
import { inspectTextContrast, measureTextContrast } from "./text-contrast";
|
|
4
|
+
import { inspectWordWrap, wordHeaders } from './word-wrap';
|
|
5
|
+
|
|
6
|
+
// Design system 1's small rules: one list, each rule a sentence a component keeps and a check that
|
|
7
|
+
// measures it on a page as it is drawn. Five presses of C show what a page breaks, in a panel at
|
|
8
|
+
// its side, for the page's owner (constraint-panel.ts). The Constraints page
|
|
9
|
+
// (website/src/content/docs/1-root/constraints.mdx) lists them all under its big rules, each
|
|
10
|
+
// component's page lists its own, and the design-system-1 skill carries them between its
|
|
11
|
+
// constraints markers, written by scripts/write-constraints.ts. tests/constraints.test.ts fails
|
|
12
|
+
// while a rule has no source test, a component no page, or the skill an old list.
|
|
13
|
+
//
|
|
14
|
+
// A new rule: add it here with its `inspect`, make the component keep it, write its source test,
|
|
15
|
+
// then run `vp run constraints:write`.
|
|
16
|
+
|
|
17
|
+
/** Where a page breaks a rule: the element, and what is wrong with it in a sentence. `review` marks what the
|
|
18
|
+
* check could not measure (words over a picture, a blend) and asks a person to look, which breaks no rule. */
|
|
19
|
+
export type Finding = { element: Element; message: string; review?: boolean };
|
|
20
|
+
|
|
21
|
+
export type Constraint = {
|
|
22
|
+
/** `<component>.<what>`, lower case, never changed once given. */
|
|
23
|
+
code: string;
|
|
24
|
+
/** The rule, one sentence, said of the thing and not of a product. */
|
|
25
|
+
rule: string;
|
|
26
|
+
/** Why it holds, when the rule alone does not say. */
|
|
27
|
+
why?: string;
|
|
28
|
+
/** The elements that keep it, by tag. */
|
|
29
|
+
components: string[];
|
|
30
|
+
/** The test that measures it in the source: a file in tests/ and the name it is given there. */
|
|
31
|
+
check: { file: string; test: string };
|
|
32
|
+
/** Measures it on a page as drawn, and returns where the page breaks it. */
|
|
33
|
+
inspect: (root: ParentNode) => Finding[];
|
|
34
|
+
/** What it measures on a page, one and many (`['card', 'cards']`), and how many of them a page has. */
|
|
35
|
+
measures: [one: string, many: string];
|
|
36
|
+
count: (root: ParentNode) => number;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/** The actual initial, including a component nested in another open shadow root. */
|
|
40
|
+
function isAvatarInitial(element: Element): boolean {
|
|
41
|
+
for (let parent: Element | null = element; parent; parent = parent.assignedSlot ?? parent.parentElement ?? (parent.getRootNode() as ShadowRoot).host ?? null) {
|
|
42
|
+
if (parent.matches('ds-avatar, .ds-avatar')) return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** A pixel either way is the same place. */
|
|
48
|
+
const near = (a: number, b: number) => Math.abs(a - b) <= 1;
|
|
49
|
+
const px = (n: number) => `${Math.round(n)}px`;
|
|
50
|
+
/** An element's style, from its own window: a page checked in a frame is measured by the frame's. */
|
|
51
|
+
const styleOf = (element: Element) => (element.ownerDocument.defaultView ?? window).getComputedStyle(element);
|
|
52
|
+
|
|
53
|
+
/** Every drawn card with its picture: `ds-card` and its slotted picture, or `.ds-card` and its media's. */
|
|
54
|
+
function cards(root: ParentNode) {
|
|
55
|
+
return Array.from(root.querySelectorAll<HTMLElement>('ds-card, .ds-card')).flatMap(card => {
|
|
56
|
+
const slotted = card.localName === 'ds-card'
|
|
57
|
+
? card.querySelector<HTMLElement>(':scope > [slot="media"]')
|
|
58
|
+
: card.querySelector<HTMLElement>(':scope > .ds-card__media > *');
|
|
59
|
+
const picture = slotted?.localName === 'picture' ? slotted.querySelector('img') ?? slotted : slotted;
|
|
60
|
+
if (!picture) return [];
|
|
61
|
+
const box = card.getBoundingClientRect();
|
|
62
|
+
const face = picture.getBoundingClientRect();
|
|
63
|
+
if (!box.height || !face.height) return [];
|
|
64
|
+
return [{ card, picture, box, face }];
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The element's parent as drawn: the slot it is given to, the host of the shadow root it is in, or its parent. */
|
|
69
|
+
function drawnParent(element: Element): Element | null {
|
|
70
|
+
if (element.assignedSlot) return element.assignedSlot;
|
|
71
|
+
const parent = element.parentNode;
|
|
72
|
+
if (parent && parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE && 'host' in parent) return (parent as ShadowRoot).host;
|
|
73
|
+
return element.parentElement;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Whether something the element stands in cuts it off or scrolls it, so it cannot widen the page: a slide in
|
|
77
|
+
* a carousel's track stands in the track, whichever side of a shadow root the two are on. */
|
|
78
|
+
function contained(element: Element) {
|
|
79
|
+
for (let box = drawnParent(element); box && box !== element.ownerDocument.documentElement; box = drawnParent(box)) {
|
|
80
|
+
const style = styleOf(box);
|
|
81
|
+
if (/hidden|clip|auto|scroll/.test(style.overflowX) || style.position === 'fixed') return true;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Every piece of the page that is drawn: it has a size. */
|
|
87
|
+
function drawn(root: ParentNode) {
|
|
88
|
+
return Array.from(root.querySelectorAll('body *')).filter(element => {
|
|
89
|
+
const box = element.getBoundingClientRect();
|
|
90
|
+
return box.width > 0 && box.height > 0;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Sideways scrolling by design: a carousel's track, a table, a block of code and a matrix scroll in their own frame. */
|
|
95
|
+
const SIDEWAYS_BY_DESIGN = '.ds-carousel__track, .ds-carousel, ds-carousel, .ds-table-scroll, .ds-table, ds-table, table, pre, ds-matrix, .ds-matrix';
|
|
96
|
+
|
|
97
|
+
/** Every box on the page that scrolls sideways: its overflow lets it, and it holds more than its width. */
|
|
98
|
+
function sidewaysScrollers(root: ParentNode) {
|
|
99
|
+
return Array.from(root.querySelectorAll<HTMLElement>('body *')).filter(element =>
|
|
100
|
+
element.clientWidth > 0 && element.scrollWidth > element.clientWidth + 1 && /auto|scroll/.test(styleOf(element).overflowX));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Cards side by side: one parent, one top and one height. */
|
|
104
|
+
function rows(found: ReturnType<typeof cards>) {
|
|
105
|
+
const groups: (typeof found)[] = [];
|
|
106
|
+
for (const entry of found) {
|
|
107
|
+
const row = groups.find(([first]) => first!.card.parentElement === entry.card.parentElement
|
|
108
|
+
&& near(first!.box.top, entry.box.top) && near(first!.box.height, entry.box.height));
|
|
109
|
+
if (row) row.push(entry); else groups.push([entry]);
|
|
110
|
+
}
|
|
111
|
+
return groups.filter(row => row.length > 1);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export const constraints: Constraint[] = [
|
|
115
|
+
{
|
|
116
|
+
code: 'page.fault-lines',
|
|
117
|
+
rule: 'Fault lines keep related card and control grids aligned across the whole page, with matching track edges and gaps at every checked viewport width.',
|
|
118
|
+
why: 'Compare separate sections in the same alignment family; responsive layouts may change together, while unrelated groups and intentional exceptions remain independent.',
|
|
119
|
+
components: ['ds-page'],
|
|
120
|
+
check: { file: 'tests/fault-lines.test.ts', test: 'fault lines catch shifted tracks and unequal gaps across sections at each viewport' },
|
|
121
|
+
measures: ['alignment group', 'alignment groups'], count: root => faultLineGrids(root).length, inspect: inspectFaultLines,
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
code: 'select.hug',
|
|
125
|
+
rule: 'Select and date-picker triggers use hug sizing, never fill sizing or growing to occupy the available row.',
|
|
126
|
+
components: ['ds-select', 'ds-calendar'],
|
|
127
|
+
check: { file: 'tests/select-hug.test.ts', test: 'select triggers hug instead of filling their row' },
|
|
128
|
+
measures: ['select', 'selects'],
|
|
129
|
+
count: root => root.querySelectorAll('ds-select[data-field]:not([data-listbox]), ds-calendar[picker]').length,
|
|
130
|
+
inspect: root => Array.from(root.querySelectorAll<HTMLElement>('ds-select[data-field]:not([data-listbox]), ds-calendar[picker]')).flatMap(element => {
|
|
131
|
+
const control = element.querySelector('select') ?? element.shadowRoot?.querySelector('.trigger');
|
|
132
|
+
if (!control || !element.getBoundingClientRect().width) return [];
|
|
133
|
+
const fills = Number(styleOf(element).flexGrow) > 0 || element.getBoundingClientRect().width > control.getBoundingClientRect().width + 1;
|
|
134
|
+
return fills ? [{ element, message: 'Use hug sizing; the select must not fill its row.' }] : [];
|
|
135
|
+
}),
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
{
|
|
139
|
+
code: 'button.visible-label',
|
|
140
|
+
rule: 'Button labels stay fully visible, never faded, gradient-masked or truncated; round the required width up to whole grid squares.',
|
|
141
|
+
why: 'The action must remain readable; a chosen width cannot cut off its label.',
|
|
142
|
+
components: ['ds-button'],
|
|
143
|
+
check: { file: 'tests/button-cut-fade-measure.test.ts', test: 'button labels stay whole without gradients and snap up to the grid' },
|
|
144
|
+
measures: ['button', 'buttons'], count: root => labelledButtons(root).length, inspect: inspectButtonLabels,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
code: 'text.whole-words',
|
|
148
|
+
rule: 'Headings and table headers keep each word intact across lines; only a long word that cannot fit may split at a valid language-specific hyphenation point, with a visible hyphen.',
|
|
149
|
+
why: 'Wrap between words or give the heading more room; never leave a final letter on its own, use break-all or anywhere, or insert an arbitrary line break inside a word. Use the correct lang with automatic hyphenation, or a soft hyphen at an editorially verified break; scripts with other line-breaking conventions keep their own typesetting rules.',
|
|
150
|
+
components: ['ds-text', 'ds-table'],
|
|
151
|
+
check: { file: 'tests/word-wrap.test.ts', test: 'headings and table headers keep whole words or use a typesetting hyphen' },
|
|
152
|
+
measures: ['heading or table header', 'headings and table headers'],
|
|
153
|
+
count: root => wordHeaders(root).length,
|
|
154
|
+
inspect: inspectWordWrap,
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
code: 'color.text-contrast',
|
|
158
|
+
rule: 'Visible text, including muted labels, placeholders and avatar initials, has at least 4.5:1 contrast against the background it is drawn on.',
|
|
159
|
+
why: 'Measure resolved colours through slots and open shadow roots, including transparency and opacity; image backgrounds and effects that cannot be measured need a visual review.',
|
|
160
|
+
components: ['ds-color', 'ds-text', 'ds-avatar'],
|
|
161
|
+
check: { file: 'tests/text-contrast.test.ts', test: 'rendered text contrast catches component overrides, slots, alpha and opacity' },
|
|
162
|
+
measures: ['text sample', 'text samples'],
|
|
163
|
+
count: root => measureTextContrast(root).length,
|
|
164
|
+
inspect: inspectTextContrast,
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
code: 'avatar.initial-contrast',
|
|
168
|
+
rule: 'Avatar initials have at least 4.5:1 contrast against their rendered fill, including after a theme or identity colour changes.',
|
|
169
|
+
why: 'Check each initial inside the avatar’s shadow root and in overlapping groups; decorative initials still need to be readable. Explicit ink overrides must pass too.',
|
|
170
|
+
components: ['ds-avatar'],
|
|
171
|
+
check: { file: 'tests/avatar-contrast.test.ts', test: 'avatar initials stay readable after theme and CSS-variable changes, and the avatar constraint catches overrides' },
|
|
172
|
+
measures: ['avatar initial', 'avatar initials'],
|
|
173
|
+
count: root => measureTextContrast(root).filter(result => isAvatarInitial(result.element)).length,
|
|
174
|
+
inspect: root => inspectTextContrast(root).filter(finding => isAvatarInitial(finding.element)),
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
code: 'card.media-level',
|
|
178
|
+
rule: "A card's picture stands on the card's foot, a square below the words at least, so the pictures of cards side by side stand on one level whatever the words above them.",
|
|
179
|
+
why: 'The room a taller card leaves goes above its picture, never below it, so pictures of one height side by side start on one line too; a taller picture starts higher.',
|
|
180
|
+
components: ['ds-card'],
|
|
181
|
+
check: { file: 'tests/card.test.ts', test: "a picture stands on the card's foot, so pictures side by side stand on one level" },
|
|
182
|
+
measures: ['card with a media picture', 'cards with a media picture'],
|
|
183
|
+
count: root => cards(root).length,
|
|
184
|
+
inspect(root) {
|
|
185
|
+
const found = cards(root);
|
|
186
|
+
const findings: Finding[] = [];
|
|
187
|
+
for (const { picture, box, face } of found) {
|
|
188
|
+
if (face.bottom < box.bottom - 1) findings.push({ element: picture, message: `The picture stands ${px(box.bottom - face.bottom)} above the card's foot.` });
|
|
189
|
+
}
|
|
190
|
+
for (const [first, ...rest] of rows(found)) {
|
|
191
|
+
for (const { picture, face } of rest) {
|
|
192
|
+
if (near(face.height, first!.face.height) && !near(face.top, first!.face.top)) findings.push({ element: picture, message: `The picture starts ${px(Math.abs(face.top - first!.face.top))} ${face.top > first!.face.top ? 'lower' : 'higher'} than the one in the first card beside it.` });
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return findings;
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
code: 'card.media-corners',
|
|
200
|
+
rule: "A picture is never rounded where it meets the card's edge: the card cuts it square there, and only a corner standing free inside the card, towards the words, keeps a picture's corner.",
|
|
201
|
+
components: ['ds-card'],
|
|
202
|
+
check: { file: 'tests/card.test.ts', test: "a picture is square where it meets the card's edge" },
|
|
203
|
+
measures: ['card with a media picture', 'cards with a media picture'],
|
|
204
|
+
count: root => cards(root).length,
|
|
205
|
+
inspect(root) {
|
|
206
|
+
return cards(root).flatMap(({ picture, box, face }) => {
|
|
207
|
+
const top = face.top <= box.top + 1;
|
|
208
|
+
const bottom = face.bottom >= box.bottom - 1;
|
|
209
|
+
const start = face.left <= box.left + 1;
|
|
210
|
+
const end = face.right >= box.right - 1;
|
|
211
|
+
const style = styleOf(picture);
|
|
212
|
+
const corners: [string, string, boolean][] = [
|
|
213
|
+
['top left', style.borderTopLeftRadius, top || start],
|
|
214
|
+
['top right', style.borderTopRightRadius, top || end],
|
|
215
|
+
['bottom right', style.borderBottomRightRadius, bottom || end],
|
|
216
|
+
['bottom left', style.borderBottomLeftRadius, bottom || start],
|
|
217
|
+
];
|
|
218
|
+
const rounded = corners.filter(([, radius, meets]) => meets && parseFloat(radius) > 0).map(([name]) => name);
|
|
219
|
+
return rounded.length ? [{ element: picture, message: `The picture's ${rounded.join(' and ')} ${rounded.length > 1 ? 'corners are' : 'corner is'} rounded where it meets the card.` }] : [];
|
|
220
|
+
});
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
code: 'page.window-width',
|
|
225
|
+
rule: 'A page is never wider than its window: nothing reaches past either edge unless something it stands in cuts it off or scrolls it, so a phone never zooms the page out or slides it sideways.',
|
|
226
|
+
why: 'A phone widens a page that is too wide and zooms it out, words and all, so a piece one square too wide spoils every piece on the page; check it at every phone width from 320px up.',
|
|
227
|
+
components: ['ds-page'],
|
|
228
|
+
check: { file: 'tests/page.test.ts', test: 'every column of a page can shrink to the window, and below thirty squares every layout stacks' },
|
|
229
|
+
measures: ['piece', 'pieces'],
|
|
230
|
+
count: root => drawn(root).length,
|
|
231
|
+
inspect(root) {
|
|
232
|
+
const page = (root as Node).ownerDocument ?? (root as Document);
|
|
233
|
+
const width = page.documentElement.clientWidth || page.defaultView?.innerWidth || 0;
|
|
234
|
+
if (!width) return [];
|
|
235
|
+
const past = (element: Element) => {
|
|
236
|
+
const box = element.getBoundingClientRect();
|
|
237
|
+
if (!box.width || !box.height) return 0;
|
|
238
|
+
return Math.max(box.right - width, -box.left, 0);
|
|
239
|
+
};
|
|
240
|
+
const findings: Finding[] = [];
|
|
241
|
+
for (const element of drawn(root)) {
|
|
242
|
+
const reach = past(element);
|
|
243
|
+
if (reach <= 1 || styleOf(element).position === 'fixed' || contained(element)) continue;
|
|
244
|
+
// Only the outermost: the element whose own box first passes the edge.
|
|
245
|
+
if (element.parentElement && past(element.parentElement) > 1) continue;
|
|
246
|
+
const box = element.getBoundingClientRect();
|
|
247
|
+
const side = box.right - width >= -box.left ? 'right' : 'left';
|
|
248
|
+
findings.push({ element, message: `It reaches ${px(reach)} past the window's ${side} edge, in a window ${px(width)} wide.` });
|
|
249
|
+
}
|
|
250
|
+
return findings;
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
code: 'carousel.sideways-scroll',
|
|
255
|
+
rule: 'Only a carousel scrolls pieces sideways: pieces wider than their column stand in a carousel strip or stack, and nothing else scrolls sideways but a table, a block of code or a matrix in its own frame.',
|
|
256
|
+
why: 'A box given overflow auto to keep a page in its window hides what is past its edge with nothing to say it is there, no snap and no controls, and a phone slides it where a finger meant to scroll down; make it a ds-carousel strip (slide-width="auto") or stack it.',
|
|
257
|
+
components: ['ds-carousel'],
|
|
258
|
+
check: { file: 'tests/carousel.test.ts', test: 'only a carousel scrolls sideways: a plain box that scrolls sideways breaks the rule, a track, a table or a block of code does not' },
|
|
259
|
+
measures: ['box that scrolls sideways', 'boxes that scroll sideways'],
|
|
260
|
+
count: root => sidewaysScrollers(root).length,
|
|
261
|
+
inspect(root) {
|
|
262
|
+
return sidewaysScrollers(root).filter(element => !element.closest(SIDEWAYS_BY_DESIGN)).map(element => ({
|
|
263
|
+
element,
|
|
264
|
+
message: `It holds ${px(element.scrollWidth)} of pieces in ${px(element.clientWidth)} and scrolls them sideways; only a carousel does that.`,
|
|
265
|
+
}));
|
|
266
|
+
},
|
|
267
|
+
},
|
|
268
|
+
];
|
|
269
|
+
|
|
270
|
+
/** Every rule a page breaks, in the list's order. */
|
|
271
|
+
export function inspectConstraints(root: ParentNode = document): (Finding & { constraint: Constraint })[] {
|
|
272
|
+
return constraints.flatMap(constraint => constraint.inspect(root).map(finding => ({ ...finding, constraint })));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Every rule with how many things it measured on the page and where the page breaks it, in the list's order. */
|
|
276
|
+
export function measureConstraints(root: ParentNode = document): { constraint: Constraint; measured: number; findings: Finding[] }[] {
|
|
277
|
+
return constraints.map(constraint => ({ constraint, measured: constraint.count(root), findings: constraint.inspect(root) }));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** The component's name as its page is called: `ds-card` is Card. */
|
|
281
|
+
export const componentName = (tag: string) => {
|
|
282
|
+
const name = tag.replace(/^ds-/, '').replace(/-/g, ' ');
|
|
283
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/** The rules grouped by the first component that keeps them, in the list's order. */
|
|
287
|
+
export function byComponent(list: Constraint[] = constraints): [string, Constraint[]][] {
|
|
288
|
+
const groups = new Map<string, Constraint[]>();
|
|
289
|
+
for (const constraint of list) {
|
|
290
|
+
const tag = constraint.components[0]!;
|
|
291
|
+
groups.set(tag, [...(groups.get(tag) ?? []), constraint]);
|
|
292
|
+
}
|
|
293
|
+
return [...groups];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export const skillStart = '<!-- constraints:start (written by scripts/write-constraints.ts from DS1/0-face/constraints.ts) -->';
|
|
297
|
+
export const skillEnd = '<!-- constraints:end -->';
|
|
298
|
+
|
|
299
|
+
/** The skill's list: a line a rule, under its component, between the markers. */
|
|
300
|
+
export function skillBlock(list: Constraint[] = constraints): string {
|
|
301
|
+
const lines = [skillStart];
|
|
302
|
+
for (const [tag, rules] of byComponent(list)) {
|
|
303
|
+
lines.push('', `**${componentName(tag)}** (\`${tag}\`)`, '');
|
|
304
|
+
for (const { code, rule, why } of rules) lines.push(`- \`${code}\`: ${rule}${why ? ` ${why}` : ''}`);
|
|
305
|
+
}
|
|
306
|
+
lines.push('', skillEnd);
|
|
307
|
+
return lines.join('\n');
|
|
308
|
+
}
|
package/DS1/0-face/copy-text.ts
CHANGED