簽、提示與自顯現(xiàn)錯(cuò)誤的狀態(tài)驅(qū)動(dòng)表單)
Yeti 表單控件 Field 組件完全指南標(biāo)簽、提示與自顯現(xiàn)錯(cuò)誤的狀態(tài)驅(qū)動(dòng)表單【免費(fèi)下載鏈接】yetiA CSS-first, native, zero-build layout and styling framework for web designers.項(xiàng)目地址: https://gitcode.com/gh_mirrors/fo/yetiYeti 是一個(gè) CSS-first、原生、零構(gòu)建的布局與樣式框架項(xiàng)目說(shuō)明而field是其中負(fù)責(zé)一個(gè)表單控件及其附屬信息的組件它把一個(gè) label、一個(gè)原生控件、一段可選 hint 提示和一段錯(cuò)誤信息捆綁為一個(gè)整體。本文以 docs/field.md 為骨架結(jié)合 field.css 源碼、manifest.json 聲明與 field.spec.js 測(cè)試完整講解 field 的用法、屬性、子元素、設(shè)計(jì)令牌與無(wú)障礙細(xì)節(jié)讓你能直接用純 HTML 類名搭建無(wú)需 JavaScript 的校驗(yàn)型表單。Field 是什么一個(gè)控件的一切Yeti 的 Field 組件定義非常凝練一個(gè)表單控件text、email、number、select、textarea、checkbox、radio、switch、range 等任意一種配上它的標(biāo)簽、可選的幫助文本以及一個(gè)當(dāng)控件無(wú)效時(shí)自己顯現(xiàn)出來(lái)的錯(cuò)誤信息。它解決的是裸 HTML 表單做不到的三件事label 與控件的關(guān)聯(lián)for?id配對(duì)幫助文本hint的排版與語(yǔ)義掛載錯(cuò)誤信息的顯示時(shí)機(jī)——既不在頁(yè)面加載時(shí)全紅一片也不需要一行腳本。在 Yeti 的實(shí)踐里一個(gè)表單通常是stack布局下若干 field 與一個(gè) button 的縱向組合field 負(fù)責(zé)承接 HTML 語(yǔ)義本身無(wú)法表達(dá)的那部分。最小示例一個(gè)帶提示與錯(cuò)誤的郵箱輸入官方文檔給出如下最典型的結(jié)構(gòu)example.html 中完全一致div classfield label foremailEmail/label input idemail typeemail required aria-describedbyemail-hint email-error p idemail-hint>.field { display: flex; flex-direction: column; gap: var(--yeti-field-gap); } .field * { margin: 0; } .field :is(label, legend) { font-weight: var(--yeti-weight-strong); font-size: var(--_yeti-size-text); }label 與 legend 使用粗體--yeti-weight-strong字號(hào)跟隨內(nèi)部令牌--_yeti-size-text由data-size決定。2. 控件讀取控件令牌主題一處改動(dòng)處處生效文本類控件input 的非特殊類型、select、textarea直接以原生元素配合令牌樣式.field :is(input:not(:where([typecheckbox], [typeradio], [typerange], [typecolor], [typefile])), select, textarea) { min-block-size: calc(var(--yeti-control-size) max(0px, var(--_yeti-size-space) - var(--yeti-space-sm))); padding: 0 var(--_yeti-size-space); font: inherit; font-size: var(--_yeti-size-text); color: var(--yeti-color-text); background-color: var(--yeti-control-surface); border: var(--yeti-border-width) solid var(--yeti-control-border); border-radius: var(--yeti-control-radius); transition: border-color var(--yeti-duration-fast) var(--yeti-ease); }這正是文檔強(qiáng)調(diào)的控件由--yeti-control-*一組令牌統(tǒng)一樣式主題只要改了--yeti-control-radius所有輸入框的圓角隨之改變。例如 soft.css 主題正是通過(guò)--yeti-control-radius: var(--yeti-radius-md);一行讓控件更圓潤(rùn)。特殊處理還包括textarea額外設(shè)置padding-block與min-block-size: 4lh約 4 行文本高度這里用到了lh單位見(jiàn)瀏覽器支持一節(jié)select使用appearance: none去掉原生外觀把背景替換為--yeti-control-chevron這個(gè)內(nèi)聯(lián) SVG 箭頭圖像并預(yù)留右側(cè) padding 防止文字壓到箭頭:focus-visible時(shí)邊框變?yōu)?-yeti-color-border-strong鍵盤焦點(diǎn)清晰可辨。3. 錯(cuò)誤顯現(xiàn)的時(shí)機(jī)::user-invalid與aria-invalid錯(cuò)誤默認(rèn)隱藏僅在兩種情況下顯現(xiàn).field [data-error] { display: none; ... } .field:has(:user-invalid, [aria-invalidtrue]) [data-error] { display: block; }客戶端校驗(yàn)控件:user-invalid命中——即用戶已經(jīng)觸碰過(guò)控件且值無(wú)效email輸入nope后失焦即觸發(fā)錯(cuò)誤顯現(xiàn)服務(wù)端往返服務(wù)器返回后你設(shè)置aria-invalidtrue錯(cuò)誤立即顯現(xiàn)無(wú)需用戶再操作。同時(shí)無(wú)效控件自身的邊框會(huì)轉(zhuǎn)為告警色--yeti-color-alert且只紅自己的控件.field:has( :user-invalid, [aria-invalidtrue], .affix :is(:user-invalid, [aria-invalidtrue])) :is(input, select, textarea), .field:has( .affix :is(:user-invalid, [aria-invalidtrue])) .affix :is(input, select) { border-color: var(--yeti-color-alert); }測(cè)試 field.spec.js 驗(yàn)證了這套行為#email-error初始display: none填入nope并 blur 后變?yōu)閎lock且邊框色改變而#name-error夾具中通過(guò)aria-invalidtrue模擬服務(wù)端錯(cuò)誤在頁(yè)面打開(kāi)時(shí)就直接顯示。另一個(gè)用例專門驗(yàn)證一個(gè)無(wú)效控件只紅自己的控件——給#p-a設(shè)aria-invalidtrue后兄弟#p-b的邊框保持不變。4. required 的視覺(jué)標(biāo)記.field:has([required]) :is(label, legend)::after { content: *; color: var(--yeti-color-alert-text); }required控件會(huì)在 label或 legend后追加一個(gè)星號(hào)標(biāo)記顏色為告警文字色。測(cè)試通過(guò)讀取getComputedStyle(..., ::after).content確認(rèn)它包含*。注意星號(hào)只是裝飾真正被輔助技術(shù)朗讀的是required屬性本身見(jiàn)無(wú)障礙一節(jié)。5. checkbox 與 radio自動(dòng)內(nèi)聯(lián) 變體色勾選checkbox 與 radio 使用appearance: none重繪尺寸為1.25em勾選時(shí).field input:is([typecheckbox], [typeradio]):checked { background-color: var(--_yeti-variant); border-color: var(--_yeti-variant); box-shadow: inset 0 0 0 0.2em var(--yeti-control-surface); }即變體色實(shí)心中心 表面色內(nèi)環(huán)的視覺(jué)效果radio 圓角為 50%。測(cè)試會(huì)驗(yàn)證勾選后背景色等于--_yeti-variant解析出的顏色、取消勾選后背景回到--yeti-control-surface。布局上checkbox/radio 所在的 field自動(dòng)變成內(nèi)聯(lián)行l(wèi)abel 在控件右側(cè)不需要任何屬性.field:is([data-inline], :has( input:is([typecheckbox], [typeradio]))) { flex-direction: row; flex-wrap: wrap; align-items: center; gap: var(--yeti-space-xs); }測(cè)試用幾何斷言確認(rèn)了label 左側(cè)大于控件右側(cè)、二者垂直居中。內(nèi)聯(lián)時(shí) hint 與 error 通過(guò)flex-basis: 100%換行獨(dú)占一行。6. fieldset 分組多個(gè)控件共享一個(gè) legend當(dāng)需要給一組選項(xiàng)如一組 radio命名時(shí)把field類直接用在fieldset上fieldset classfield legendNotify me by/legend div classfieldinput idn-email typecheckbox namenotify valueemaillabel forn-emailEmail/label/div div classfieldinput idn-sms typecheckbox namenotify valuesmslabel forn-smsText message/label/div p>fieldset.field { padding: var(--yeti-space-sm) var(--yeti-space-md); border: var(--yeti-border-width) solid var(--yeti-control-border); border-radius: var(--yeti-control-radius); } fieldset.field legend { padding-inline: var(--yeti-space-xs); }7. switch一個(gè) checkbox 加上roleswitchcheckbox 加roleswitch就變成開(kāi)關(guān)軌道 滑塊打開(kāi)時(shí)滑塊滑到末端且軌道取變體色div classfieldinput iddark typecheckbox roleswitchlabel fordarkDark mode/label/div源碼實(shí)現(xiàn)是inline-size: 2.25em的膠囊軌道--yeti-radius-full用radial-gradient畫圓形滑塊background-position從0% 50%移到100% 50%完成滑動(dòng)勾選時(shí)背景變體色、滑塊環(huán)消失box-shadow: none。測(cè)試斷言了軌道寬度大于高度的 1.5 倍、切換前后背景色與位置均變化。8. range細(xì)軌道 圓滑塊div classfieldlabel forvolumeVolume/labelinput idvolume typerange min0 max100 value40/divrange 輸入本身是一個(gè)控件的完整高度block-size: var(--yeti-control-size)保證滑塊是一個(gè)舒適的點(diǎn)擊目標(biāo)軌道只有0.25em高、圓角膠囊狀、顏色為--yeti-control-border滑塊為 1.25em 圓形、變體色帶邊框。CSS 同時(shí)書寫::-webkit-slider-*與::-moz-range-*兩套偽元素。測(cè)試指出軌道不會(huì)填充到當(dāng)前值——因?yàn)?CSS 讀不到控件的值這是文檔明確說(shuō)明的設(shè)計(jì)取舍。無(wú)障礙Accessibility文檔對(duì)無(wú)障礙給出明確要求且這些要求被 validator 與測(cè)試雙重約束label 的for必須匹配控件的idYeti 的 validator 會(huì)拒絕缺少配對(duì)的示例把hint 與 error 的 id 放進(jìn)控件的aria-describedby屏幕閱讀器就能隨控件朗讀幫助文本并在錯(cuò)誤出現(xiàn)的瞬間朗讀錯(cuò)誤服務(wù)端發(fā)現(xiàn)的錯(cuò)誤用aria-invalidtrue呈現(xiàn)required 標(biāo)記只是裝飾輔助技術(shù)真正讀到的是required屬性本身switch 就是帶roleswitch的 checkbox它的 label 即開(kāi)關(guān)的名稱range 和其他控件一樣需要 label當(dāng)數(shù)值不是人能直接說(shuō)出的內(nèi)容時(shí)應(yīng)提供aria-valuetext。這些行為在 manifest.json 的a11y.notes中原文記錄并在 field.spec.js 中通過(guò) axe 無(wú)障礙掃描has no accessibility violations與明暗兩種配色方案下的 AA 對(duì)比度測(cè)試text meets AA in light/dark得到驗(yàn)證。屬性Attributes屬性類型取值默認(rèn)說(shuō)明data-sizeenumsm,md,lgmd縮放控件的高度與文字。data-inlineboolean——把 label 放到控件旁邊。checkbox 與 radio 不需要它本身就內(nèi)聯(lián)。data-variantenumprimary,secondary,success,warning,alert,neutralprimary勾選后的 checkbox / radio 的顏色。屬性背后的機(jī)制從源碼結(jié)構(gòu)看這些屬性不是 field 特有的規(guī)則而是復(fù)用 attributes.css 中一套通用的布局屬性解析data-variant為元素掛載--_yeti-variant等一組變體色階梯變量取值集合定義在 vocabulary.json 的variant詞條中field 通過(guò):not([data-variant])設(shè)置默認(rèn)的 primary 變體所以不會(huì)繼承父元素的變體data-size解析為兩個(gè)內(nèi)部令牌--_yeti-size-text文字步進(jìn)與--_yeti-size-space間距步進(jìn)例如lg對(duì)應(yīng)--yeti-text-lg與--yeti-space-mdsm則更緊湊。field.css 中控件的min-block-size與 padding 都讀取這兩個(gè)令牌從而整體縮放data-inline是布爾屬性僅用于把 label 排到控件旁邊對(duì) checkbox/radio 屬冗余寫法因?yàn)?has()已自動(dòng)處理。size-control詞條位于 vocabulary.json 中值為[sm, md, lg]。子元素Childrenmanifest.json以 selector形式聲明了 field 允許的子元素結(jié)構(gòu)每個(gè)最多一個(gè) label01 個(gè)。labelfor指向控件的 id除非是帶 legend 的 fieldset否則必須存在。 legend01 個(gè)。當(dāng) field 是分組多個(gè)控件的 fieldset 時(shí)使用。 input01 個(gè)??丶?。 select01 個(gè)??丶?textarea01 個(gè)??丶?.affix01 個(gè)。控件槽位交給 affix 組件——即帶附屬物的控件或兩個(gè)控件拼接參見(jiàn) affix 組件 與 affix.css。 [data-hint]01 個(gè)。幫助文本由控件的aria-describedby引用。 [data-error]01 個(gè)。錯(cuò)誤消息控件無(wú)效前保持隱藏。hint 與 error 的樣式在源碼中分別為--yeti-text-sm字號(hào)hint 用--yeti-color-text-muted弱化error 用--yeti-color-alert-text告警色。設(shè)計(jì)令牌Tokens令牌說(shuō)明--yeti-field-gaplabel、控件與 hint 之間的間距。--yeti-control-size控件的最小高度。--yeti-control-radius控件的圓角。--yeti-control-border控件靜止時(shí)的邊框。--yeti-control-surface控件的背景。--yeti-control-chevronselect 的箭頭圖像。--yeti-color-alert無(wú)效控件的邊框顏色。默認(rèn)值與主題化這些令牌的默認(rèn)值定義在 surface.css 與 components.css并匯總于 tokens.json 的control分組--yeti-control-size: 2.5remmd尺寸下按鈕或輸入的最小塊高--yeti-control-radius: var(--yeti-radius-sm)--yeti-control-border: var(--yeti-color-border)--yeti-control-surface: var(--yeti-color-surface)--yeti-control-chevron一個(gè)中灰色內(nèi)聯(lián) SVG 箭頭主題可為深色控件表面提供更亮的箭頭如 tokens.json 中的說(shuō)明--yeti-field-gap: var(--yeti-space-xs)。--yeti-color-alert定義于 color.css是一個(gè)隨明暗模式切換的light-dark()顏色淺色下約#af3c3a。正如文檔所說(shuō)主題只要覆蓋--yeti-control-radius如 soft.css 改為radius-md即可讓全部控件變圓潤(rùn)——這正是控件讀取控制令牌設(shè)計(jì)帶來(lái)的杠桿效應(yīng)。內(nèi)部令牌可能在次要版本間變化以下令牌以下劃線開(kāi)頭是內(nèi)部實(shí)現(xiàn)細(xì)節(jié)不建議在主題中依賴--_yeti-variant--_yeti-on-variant--_yeti-size-text--_yeti-size-space瀏覽器支持無(wú)守衛(wèi)直接使用:has()、:user-invalid、appearance: none、lh單位supports守衛(wèi)之后無(wú)。這意味著 field 依賴現(xiàn)代瀏覽器的原生能力:user-invalid是 Chromium 111 / Firefox 88 / Safari 16.5 支持的偽類但換來(lái)的是零腳本的校驗(yàn)體驗(yàn)。若目標(biāo)瀏覽器較舊需要自行評(píng)估降級(jí)方案。JavaScript無(wú)。該組件純 CSS 實(shí)現(xiàn)CSS only自 7.0.0 版本起可用。錯(cuò)誤顯隱、開(kāi)關(guān)滑塊、range 滑塊全部由 CSS 狀態(tài)與偽元素完成這也是 field 在 field.spec.js 中能通過(guò)純?yōu)g覽器斷言驗(yàn)證無(wú) mock、無(wú)注入腳本的原因。小結(jié)Yeti 的 field 組件展示了CSS-first表單設(shè)計(jì)的完整路徑用:has():user-invalid把校驗(yàn)狀態(tài)從控件本身映射到錯(cuò)誤文本與邊框用appearance: none重繪 checkbox、radio、switch、range用控制令牌統(tǒng)一所有控件的尺寸與圓角再以 validator 強(qiáng)制 label?id 配對(duì)保證無(wú)障礙底線。按本文的示例與屬性說(shuō)明你可以直接用語(yǔ)義化 HTML 搭建出無(wú)腳本、可訪問(wèn)、可主題化的完整表單。【免費(fèi)下載鏈接】yetiA CSS-first, native, zero-build layout and styling framework for web designers.項(xiàng)目地址: https://gitcode.com/gh_mirrors/fo/yeti創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考