[{"data":1,"prerenderedAt":1988},["ShallowReactive",2],{"en-post-\u002Fen\u002Fhtml-features-without-javascript":3},{"id":4,"title":5,"body":6,"description":1977,"extension":1978,"meta":1979,"navigation":347,"path":1983,"seo":1984,"sitemap":1985,"stem":1986,"__hash__":1987},"blogEn\u002Fen\u002Fhtml-features-without-javascript.md","Things HTML Can Do Without JavaScript: Stop Building Everything Out of divs",{"type":7,"value":8,"toc":1958},"minimark",[9,14,18,58,66,82,88,95,98,109,172,194,289,300,302,310,316,419,422,493,500,502,509,520,667,705,758,780,782,789,792,948,955,957,965,972,1074,1137,1155,1157,1161,1164,1283,1323,1357,1367,1369,1373,1469,1528,1545,1547,1551,1561,1673,1693,1695,1702,1705,1804,1820,1822,1826,1923,1926,1947,1954],[10,11,13],"h2",{"id":12},"how-many-lines-does-an-accordion-take","How many lines does an accordion take?",[15,16,17],"p",{},"Say you're building a \"click to expand\" FAQ. It usually starts like this:",[19,20,21,30,33,40,51],"ul",{},[22,23,24,25,29],"li",{},"Build the title and content out of ",[26,27,28],"code",{},"div","s",[22,31,32],{},"Attach a click handler",[22,34,35,36,39],{},"Track an ",[26,37,38],{},"isOpen"," state",[22,41,42,43,46,47,50],{},"Add ",[26,44,45],{},"tabindex"," and a ",[26,48,49],{},"keydown"," handler so it opens from the keyboard",[22,52,53,54,57],{},"Keep ",[26,55,56],{},"aria-expanded"," in sync for screen readers",[15,59,60,61,65],{},"But HTML already has a way to do this with ",[62,63,64],"strong",{},"two tags",". The example right below actually works. Try clicking it.",[67,68,69,73],"details",{},[70,71,72],"summary",{},"Is this accordion really HTML only?",[15,74,75,76,78,79,81],{},"Yes. Not a single line of JavaScript, just the ",[26,77,67],{}," and ",[26,80,70],{}," tags. It opens from the keyboard (Tab → Enter), and screen readers announce whether it's expanded or collapsed.",[83,84,85],"blockquote",{},[15,86,87],{},"Browsers give you far more out of the box than most people expect. Before building something yourself, check whether HTML already has it. You'll write less code, and accessibility and performance come for free.",[15,89,90,91,94],{},"This post collects those ",[62,92,93],{},"features you get from HTML alone",".",[96,97],"hr",{},[10,99,101,102,105,106],{"id":100},"_1-accordion-details-summary","1. Accordion: ",[26,103,104],{},"\u003Cdetails>"," + ",[26,107,108],{},"\u003Csummary>",[110,111,116],"pre",{"className":112,"code":113,"language":114,"meta":115,"style":115},"language-html shiki shiki-themes github-light github-dark","\u003Cdetails>\n  \u003Csummary>How long does shipping take?\u003C\u002Fsummary>\n  \u003Cp>1–3 business days.\u003C\u002Fp>\n\u003C\u002Fdetails>\n","html","",[26,117,118,133,148,162],{"__ignoreMap":115},[119,120,123,127,130],"span",{"class":121,"line":122},"line",1,[119,124,126],{"class":125},"sVt8B","\u003C",[119,128,67],{"class":129},"s9eBZ",[119,131,132],{"class":125},">\n",[119,134,136,139,141,144,146],{"class":121,"line":135},2,[119,137,138],{"class":125},"  \u003C",[119,140,70],{"class":129},[119,142,143],{"class":125},">How long does shipping take?\u003C\u002F",[119,145,70],{"class":129},[119,147,132],{"class":125},[119,149,151,153,155,158,160],{"class":121,"line":150},3,[119,152,138],{"class":125},[119,154,15],{"class":129},[119,156,157],{"class":125},">1–3 business days.\u003C\u002F",[119,159,15],{"class":129},[119,161,132],{"class":125},[119,163,165,168,170],{"class":121,"line":164},4,[119,166,167],{"class":125},"\u003C\u002F",[119,169,67],{"class":129},[119,171,132],{"class":125},[19,173,174,181],{},[22,175,176,177,180],{},"Add the ",[26,178,179],{},"open"," attribute to start expanded.",[22,182,183,185,186,189,190,193],{},[26,184,67],{}," elements that share the same ",[26,187,188],{},"name"," attribute become an ",[62,191,192],{},"exclusive accordion",": opening one closes the others automatically.",[110,195,197],{"className":112,"code":196,"language":114,"meta":115,"style":115},"\u003Cdetails name=\"faq\">\u003Csummary>Question 1\u003C\u002Fsummary>Answer 1\u003C\u002Fdetails>\n\u003Cdetails name=\"faq\">\u003Csummary>Question 2\u003C\u002Fsummary>Answer 2\u003C\u002Fdetails>\n\u003Cdetails name=\"faq\">\u003Csummary>Question 3\u003C\u002Fsummary>Answer 3\u003C\u002Fdetails>\n",[26,198,199,233,261],{"__ignoreMap":115},[119,200,201,203,205,209,212,216,219,221,224,226,229,231],{"class":121,"line":122},[119,202,126],{"class":125},[119,204,67],{"class":129},[119,206,208],{"class":207},"sScJk"," name",[119,210,211],{"class":125},"=",[119,213,215],{"class":214},"sZZnC","\"faq\"",[119,217,218],{"class":125},">\u003C",[119,220,70],{"class":129},[119,222,223],{"class":125},">Question 1\u003C\u002F",[119,225,70],{"class":129},[119,227,228],{"class":125},">Answer 1\u003C\u002F",[119,230,67],{"class":129},[119,232,132],{"class":125},[119,234,235,237,239,241,243,245,247,249,252,254,257,259],{"class":121,"line":135},[119,236,126],{"class":125},[119,238,67],{"class":129},[119,240,208],{"class":207},[119,242,211],{"class":125},[119,244,215],{"class":214},[119,246,218],{"class":125},[119,248,70],{"class":129},[119,250,251],{"class":125},">Question 2\u003C\u002F",[119,253,70],{"class":129},[119,255,256],{"class":125},">Answer 2\u003C\u002F",[119,258,67],{"class":129},[119,260,132],{"class":125},[119,262,263,265,267,269,271,273,275,277,280,282,285,287],{"class":121,"line":150},[119,264,126],{"class":125},[119,266,67],{"class":129},[119,268,208],{"class":207},[119,270,211],{"class":125},[119,272,215],{"class":214},[119,274,218],{"class":125},[119,276,70],{"class":129},[119,278,279],{"class":125},">Question 3\u003C\u002F",[119,281,70],{"class":129},[119,283,284],{"class":125},">Answer 3\u003C\u002F",[119,286,67],{"class":129},[119,288,132],{"class":125},[290,291,292],"tip",{},[83,293,294],{},[15,295,296,297,299],{},"The ",[26,298,188],{}," attribute has been supported in all major browsers (Chrome, Safari, Firefox) since 2024. Browsers without support simply treat it as an accordion where several items can be open, so nothing breaks.",[96,301],{},[10,303,305,306,309],{"id":304},"_2-popovers-the-popover-attribute","2. Popovers: the ",[26,307,308],{},"popover"," attribute",[15,311,312,313,315],{},"Dropdown menus, tooltips, notification panels, anything that \"pops up when you press a button\" can be done with the ",[26,314,308],{}," attribute.",[110,317,319],{"className":112,"code":318,"language":114,"meta":115,"style":115},"\u003Cbutton popovertarget=\"menu\">Open menu\u003C\u002Fbutton>\n\n\u003Cdiv id=\"menu\" popover>\n  \u003Ca href=\"\u002Fprofile\">Profile\u003C\u002Fa>\n  \u003Ca href=\"\u002Flogout\">Log out\u003C\u002Fa>\n\u003C\u002Fdiv>\n",[26,320,321,343,349,367,389,410],{"__ignoreMap":115},[119,322,323,325,328,331,333,336,339,341],{"class":121,"line":122},[119,324,126],{"class":125},[119,326,327],{"class":129},"button",[119,329,330],{"class":207}," popovertarget",[119,332,211],{"class":125},[119,334,335],{"class":214},"\"menu\"",[119,337,338],{"class":125},">Open menu\u003C\u002F",[119,340,327],{"class":129},[119,342,132],{"class":125},[119,344,345],{"class":121,"line":135},[119,346,348],{"emptyLinePlaceholder":347},true,"\n",[119,350,351,353,355,358,360,362,365],{"class":121,"line":150},[119,352,126],{"class":125},[119,354,28],{"class":129},[119,356,357],{"class":207}," id",[119,359,211],{"class":125},[119,361,335],{"class":214},[119,363,364],{"class":207}," popover",[119,366,132],{"class":125},[119,368,369,371,374,377,379,382,385,387],{"class":121,"line":164},[119,370,138],{"class":125},[119,372,373],{"class":129},"a",[119,375,376],{"class":207}," href",[119,378,211],{"class":125},[119,380,381],{"class":214},"\"\u002Fprofile\"",[119,383,384],{"class":125},">Profile\u003C\u002F",[119,386,373],{"class":129},[119,388,132],{"class":125},[119,390,392,394,396,398,400,403,406,408],{"class":121,"line":391},5,[119,393,138],{"class":125},[119,395,373],{"class":129},[119,397,376],{"class":207},[119,399,211],{"class":125},[119,401,402],{"class":214},"\"\u002Flogout\"",[119,404,405],{"class":125},">Log out\u003C\u002F",[119,407,373],{"class":129},[119,409,132],{"class":125},[119,411,413,415,417],{"class":121,"line":412},6,[119,414,167],{"class":125},[119,416,28],{"class":129},[119,418,132],{"class":125},[15,420,421],{},"That alone gets the browser to handle a surprising amount for you:",[423,424,425,441],"table",{},[426,427,428],"thead",{},[429,430,431,435],"tr",{},[432,433,434],"th",{},"What you used to build yourself",[432,436,437,438,440],{},"What ",[26,439,308],{}," does by default",[442,443,444,456,464,475,486],"tbody",{},[429,445,446,450],{},[447,448,449],"td",{},"Open\u002Fclose state",[447,451,296,452,455],{},[26,453,454],{},"popovertarget"," button toggles it",[429,457,458,461],{},[447,459,460],{},"Close on outside click",[447,462,463],{},"Built in (light dismiss)",[429,465,466,472],{},[447,467,468,469],{},"Close on ",[26,470,471],{},"Esc",[447,473,474],{},"Built in",[429,476,477,483],{},[447,478,479,482],{},[26,480,481],{},"z-index"," battles",[447,484,485],{},"Rendered in the top layer, so it's always on top",[429,487,488,491],{},[447,489,490],{},"Close the previous popover when another opens",[447,492,474],{},[15,494,495,496,499],{},"If you've ever written ",[26,497,498],{},"z-index: 99999",", the second-to-last row should feel especially good.",[96,501],{},[10,503,505,506],{"id":504},"_3-modals-dialog","3. Modals: ",[26,507,508],{},"\u003Cdialog>",[15,510,511,512,515,516,519],{},"For windows that ",[62,513,514],{},"block the page behind them",", like confirmations or login modals, use ",[26,517,518],{},"dialog",". You need one line of JavaScript, just to open it.",[110,521,523],{"className":112,"code":522,"language":114,"meta":115,"style":115},"\u003Cbutton onclick=\"confirmDialog.showModal()\">Delete\u003C\u002Fbutton>\n\n\u003Cdialog id=\"confirmDialog\">\n  \u003Cp>Are you sure you want to delete this?\u003C\u002Fp>\n  \u003Cform method=\"dialog\">\n    \u003Cbutton value=\"cancel\">Cancel\u003C\u002Fbutton>\n    \u003Cbutton value=\"ok\">Delete\u003C\u002Fbutton>\n  \u003C\u002Fform>\n\u003C\u002Fdialog>\n",[26,524,525,557,561,576,589,606,628,648,658],{"__ignoreMap":115},[119,526,527,529,531,534,536,539,542,544,547,550,553,555],{"class":121,"line":122},[119,528,126],{"class":125},[119,530,327],{"class":129},[119,532,533],{"class":207}," onclick",[119,535,211],{"class":125},[119,537,538],{"class":214},"\"",[119,540,541],{"class":125},"confirmDialog",[119,543,94],{"class":214},[119,545,546],{"class":207},"showModal",[119,548,549],{"class":214},"()\"",[119,551,552],{"class":125},">Delete\u003C\u002F",[119,554,327],{"class":129},[119,556,132],{"class":125},[119,558,559],{"class":121,"line":135},[119,560,348],{"emptyLinePlaceholder":347},[119,562,563,565,567,569,571,574],{"class":121,"line":150},[119,564,126],{"class":125},[119,566,518],{"class":129},[119,568,357],{"class":207},[119,570,211],{"class":125},[119,572,573],{"class":214},"\"confirmDialog\"",[119,575,132],{"class":125},[119,577,578,580,582,585,587],{"class":121,"line":164},[119,579,138],{"class":125},[119,581,15],{"class":129},[119,583,584],{"class":125},">Are you sure you want to delete this?\u003C\u002F",[119,586,15],{"class":129},[119,588,132],{"class":125},[119,590,591,593,596,599,601,604],{"class":121,"line":391},[119,592,138],{"class":125},[119,594,595],{"class":129},"form",[119,597,598],{"class":207}," method",[119,600,211],{"class":125},[119,602,603],{"class":214},"\"dialog\"",[119,605,132],{"class":125},[119,607,608,611,613,616,618,621,624,626],{"class":121,"line":412},[119,609,610],{"class":125},"    \u003C",[119,612,327],{"class":129},[119,614,615],{"class":207}," value",[119,617,211],{"class":125},[119,619,620],{"class":214},"\"cancel\"",[119,622,623],{"class":125},">Cancel\u003C\u002F",[119,625,327],{"class":129},[119,627,132],{"class":125},[119,629,631,633,635,637,639,642,644,646],{"class":121,"line":630},7,[119,632,610],{"class":125},[119,634,327],{"class":129},[119,636,615],{"class":207},[119,638,211],{"class":125},[119,640,641],{"class":214},"\"ok\"",[119,643,552],{"class":125},[119,645,327],{"class":129},[119,647,132],{"class":125},[119,649,651,654,656],{"class":121,"line":650},8,[119,652,653],{"class":125},"  \u003C\u002F",[119,655,595],{"class":129},[119,657,132],{"class":125},[119,659,661,663,665],{"class":121,"line":660},9,[119,662,167],{"class":125},[119,664,518],{"class":129},[119,666,132],{"class":125},[19,668,669,676,694,699],{},[22,670,671,672,675],{},"Opening it with ",[26,673,674],{},"showModal()"," blocks clicks on the background and traps focus inside the modal.",[22,677,678,679,682,683,686,687,690,691,94],{},"Pressing a button inside ",[26,680,681],{},"form method=\"dialog\""," ",[62,684,685],{},"closes it automatically",", and the pressed button's ",[26,688,689],{},"value"," ends up in ",[26,692,693],{},"dialog.returnValue",[22,695,696,698],{},[26,697,471],{}," closes it by default.",[22,700,701,702,94],{},"Style the background with CSS ",[26,703,704],{},"::backdrop",[110,706,710],{"className":707,"code":708,"language":709,"meta":115,"style":115},"language-css shiki shiki-themes github-light github-dark","dialog::backdrop {\n  background: rgb(0 0 0 \u002F 0.5);\n}\n","css",[26,711,712,721,753],{"__ignoreMap":115},[119,713,714,716,718],{"class":121,"line":122},[119,715,518],{"class":129},[119,717,704],{"class":207},[119,719,720],{"class":125}," {\n",[119,722,723,727,730,733,736,739,742,744,747,750],{"class":121,"line":135},[119,724,726],{"class":725},"sj4cs","  background",[119,728,729],{"class":125},": ",[119,731,732],{"class":725},"rgb",[119,734,735],{"class":125},"(",[119,737,738],{"class":725},"0",[119,740,741],{"class":725}," 0",[119,743,741],{"class":725},[119,745,746],{"class":125}," \u002F ",[119,748,749],{"class":725},"0.5",[119,751,752],{"class":125},");\n",[119,754,755],{"class":121,"line":150},[119,756,757],{"class":125},"}\n",[759,760,761],"note",{},[83,762,763],{},[15,764,765,766,768,769,771,772],{},"Not sure whether to use ",[26,767,308],{}," or ",[26,770,518],{},"? Remember: ",[62,773,774,775,777,778,94],{},"if it must block the page behind it, use ",[26,776,518],{}," (modal); if it just floats on top without blocking, use ",[26,779,308],{},[96,781],{},[10,783,785,786],{"id":784},"_4-autocomplete-datalist","4. Autocomplete: ",[26,787,788],{},"\u003Cdatalist>",[15,790,791],{},"Showing suggestions in a search box doesn't need a library either.",[110,793,795],{"className":112,"code":794,"language":114,"meta":115,"style":115},"\u003Clabel for=\"framework\">Framework\u003C\u002Flabel>\n\u003Cinput id=\"framework\" list=\"frameworks\" \u002F>\n\n\u003Cdatalist id=\"frameworks\">\n  \u003Coption value=\"Nuxt\">\u003C\u002Foption>\n  \u003Coption value=\"Next.js\">\u003C\u002Foption>\n  \u003Coption value=\"SvelteKit\">\u003C\u002Foption>\n  \u003Coption value=\"Astro\">\u003C\u002Foption>\n\u003C\u002Fdatalist>\n",[26,796,797,819,843,847,862,883,902,921,940],{"__ignoreMap":115},[119,798,799,801,804,807,809,812,815,817],{"class":121,"line":122},[119,800,126],{"class":125},[119,802,803],{"class":129},"label",[119,805,806],{"class":207}," for",[119,808,211],{"class":125},[119,810,811],{"class":214},"\"framework\"",[119,813,814],{"class":125},">Framework\u003C\u002F",[119,816,803],{"class":129},[119,818,132],{"class":125},[119,820,821,823,826,828,830,832,835,837,840],{"class":121,"line":135},[119,822,126],{"class":125},[119,824,825],{"class":129},"input",[119,827,357],{"class":207},[119,829,211],{"class":125},[119,831,811],{"class":214},[119,833,834],{"class":207}," list",[119,836,211],{"class":125},[119,838,839],{"class":214},"\"frameworks\"",[119,841,842],{"class":125}," \u002F>\n",[119,844,845],{"class":121,"line":150},[119,846,348],{"emptyLinePlaceholder":347},[119,848,849,851,854,856,858,860],{"class":121,"line":164},[119,850,126],{"class":125},[119,852,853],{"class":129},"datalist",[119,855,357],{"class":207},[119,857,211],{"class":125},[119,859,839],{"class":214},[119,861,132],{"class":125},[119,863,864,866,869,871,873,876,879,881],{"class":121,"line":391},[119,865,138],{"class":125},[119,867,868],{"class":129},"option",[119,870,615],{"class":207},[119,872,211],{"class":125},[119,874,875],{"class":214},"\"Nuxt\"",[119,877,878],{"class":125},">\u003C\u002F",[119,880,868],{"class":129},[119,882,132],{"class":125},[119,884,885,887,889,891,893,896,898,900],{"class":121,"line":412},[119,886,138],{"class":125},[119,888,868],{"class":129},[119,890,615],{"class":207},[119,892,211],{"class":125},[119,894,895],{"class":214},"\"Next.js\"",[119,897,878],{"class":125},[119,899,868],{"class":129},[119,901,132],{"class":125},[119,903,904,906,908,910,912,915,917,919],{"class":121,"line":630},[119,905,138],{"class":125},[119,907,868],{"class":129},[119,909,615],{"class":207},[119,911,211],{"class":125},[119,913,914],{"class":214},"\"SvelteKit\"",[119,916,878],{"class":125},[119,918,868],{"class":129},[119,920,132],{"class":125},[119,922,923,925,927,929,931,934,936,938],{"class":121,"line":650},[119,924,138],{"class":125},[119,926,868],{"class":129},[119,928,615],{"class":207},[119,930,211],{"class":125},[119,932,933],{"class":214},"\"Astro\"",[119,935,878],{"class":125},[119,937,868],{"class":129},[119,939,132],{"class":125},[119,941,942,944,946],{"class":121,"line":660},[119,943,167],{"class":125},[119,945,853],{"class":129},[119,947,132],{"class":125},[15,949,950,951,954],{},"The list filters as you type, and users can still enter values that aren't in it. You can't restyle it much, so it's a better fit for ",[62,952,953],{},"admin pages and internal tools"," than for a product's main search bar.",[96,956],{},[10,958,960,961,964],{"id":959},"_5-input-attributes-that-take-care-of-mobile-keyboards","5. ",[26,962,963],{},"\u003Cinput>"," attributes that take care of mobile keyboards",[15,966,967,968,971],{},"If you take every input with ",[26,969,970],{},"type=\"text\"",", mobile users are switching keyboards by hand every time.",[423,973,974,987],{},[426,975,976],{},[429,977,978,981,984],{},[432,979,980],{},"Attribute",[432,982,983],{},"Effect",[432,985,986],{},"Use it for",[442,988,989,1006,1019,1032,1045,1058],{},[429,990,991,996,1003],{},[447,992,993],{},[26,994,995],{},"type=\"email\"",[447,997,998,999,1002],{},"Keyboard with ",[26,1000,1001],{},"@"," + format check",[447,1004,1005],{},"Email",[429,1007,1008,1013,1016],{},[447,1009,1010],{},[26,1011,1012],{},"type=\"tel\"",[447,1014,1015],{},"Phone keypad",[447,1017,1018],{},"Phone numbers",[429,1020,1021,1026,1029],{},[447,1022,1023],{},[26,1024,1025],{},"inputmode=\"numeric\"",[447,1027,1028],{},"Number keypad (value stays a string)",[447,1030,1031],{},"Verification codes, card numbers",[429,1033,1034,1039,1042],{},[447,1035,1036],{},[26,1037,1038],{},"enterkeyhint=\"search\"",[447,1040,1041],{},"The keyboard's Enter key becomes \"Search\"",[447,1043,1044],{},"Search boxes",[429,1046,1047,1052,1055],{},[447,1048,1049],{},[26,1050,1051],{},"autocomplete=\"one-time-code\"",[447,1053,1054],{},"Suggests the code from an SMS above the keyboard",[447,1056,1057],{},"Phone verification",[429,1059,1060,1068,1071],{},[447,1061,1062,746,1065],{},[26,1063,1064],{},"type=\"date\"",[26,1066,1067],{},"type=\"color\"",[447,1069,1070],{},"Native date \u002F color pickers",[447,1072,1073],{},"Simple date and color input",[110,1075,1077],{"className":112,"code":1076,"language":114,"meta":115,"style":115},"\u003C!-- Field for a 6-digit code received by SMS -->\n\u003Cinput\n  inputmode=\"numeric\"\n  autocomplete=\"one-time-code\"\n  maxlength=\"6\"\n  enterkeyhint=\"done\"\n\u002F>\n",[26,1078,1079,1085,1092,1102,1112,1122,1132],{"__ignoreMap":115},[119,1080,1081],{"class":121,"line":122},[119,1082,1084],{"class":1083},"sJ8bj","\u003C!-- Field for a 6-digit code received by SMS -->\n",[119,1086,1087,1089],{"class":121,"line":135},[119,1088,126],{"class":125},[119,1090,1091],{"class":129},"input\n",[119,1093,1094,1097,1099],{"class":121,"line":150},[119,1095,1096],{"class":207},"  inputmode",[119,1098,211],{"class":125},[119,1100,1101],{"class":214},"\"numeric\"\n",[119,1103,1104,1107,1109],{"class":121,"line":164},[119,1105,1106],{"class":207},"  autocomplete",[119,1108,211],{"class":125},[119,1110,1111],{"class":214},"\"one-time-code\"\n",[119,1113,1114,1117,1119],{"class":121,"line":391},[119,1115,1116],{"class":207},"  maxlength",[119,1118,211],{"class":125},[119,1120,1121],{"class":214},"\"6\"\n",[119,1123,1124,1127,1129],{"class":121,"line":412},[119,1125,1126],{"class":207},"  enterkeyhint",[119,1128,211],{"class":125},[119,1130,1131],{"class":214},"\"done\"\n",[119,1133,1134],{"class":121,"line":630},[119,1135,1136],{"class":125},"\u002F>\n",[1138,1139,1140],"warning",{},[83,1141,1142],{},[15,1143,1144,1145,1148,1149,1151,1152,1154],{},"Don't use ",[26,1146,1147],{},"type=\"number\""," for card numbers or verification codes. Leading ",[26,1150,738],{},"s disappear, and the mouse wheel can change the value. For \"a string that just needs a number keypad,\" ",[26,1153,1025],{}," is the right answer.",[96,1156],{},[10,1158,1160],{"id":1159},"_6-form-validation-attributes-one-line-of-css","6. Form validation: attributes + one line of CSS",[15,1162,1163],{},"A lot of input validation can be done with HTML attributes.",[110,1165,1167],{"className":112,"code":1166,"language":114,"meta":115,"style":115},"\u003Cform>\n  \u003Cinput type=\"email\" required placeholder=\"Email\" \u002F>\n  \u003Cinput\n    type=\"password\"\n    required\n    minlength=\"8\"\n    pattern=\"(?=.*\\d)(?=.*[a-zA-Z]).*\"\n    title=\"Include both letters and numbers\"\n  \u002F>\n  \u003Cbutton>Sign up\u003C\u002Fbutton>\n\u003C\u002Fform>\n",[26,1168,1169,1177,1204,1210,1220,1225,1235,1245,1255,1260,1274],{"__ignoreMap":115},[119,1170,1171,1173,1175],{"class":121,"line":122},[119,1172,126],{"class":125},[119,1174,595],{"class":129},[119,1176,132],{"class":125},[119,1178,1179,1181,1183,1186,1188,1191,1194,1197,1199,1202],{"class":121,"line":135},[119,1180,138],{"class":125},[119,1182,825],{"class":129},[119,1184,1185],{"class":207}," type",[119,1187,211],{"class":125},[119,1189,1190],{"class":214},"\"email\"",[119,1192,1193],{"class":207}," required",[119,1195,1196],{"class":207}," placeholder",[119,1198,211],{"class":125},[119,1200,1201],{"class":214},"\"Email\"",[119,1203,842],{"class":125},[119,1205,1206,1208],{"class":121,"line":150},[119,1207,138],{"class":125},[119,1209,1091],{"class":129},[119,1211,1212,1215,1217],{"class":121,"line":164},[119,1213,1214],{"class":207},"    type",[119,1216,211],{"class":125},[119,1218,1219],{"class":214},"\"password\"\n",[119,1221,1222],{"class":121,"line":391},[119,1223,1224],{"class":207},"    required\n",[119,1226,1227,1230,1232],{"class":121,"line":412},[119,1228,1229],{"class":207},"    minlength",[119,1231,211],{"class":125},[119,1233,1234],{"class":214},"\"8\"\n",[119,1236,1237,1240,1242],{"class":121,"line":630},[119,1238,1239],{"class":207},"    pattern",[119,1241,211],{"class":125},[119,1243,1244],{"class":214},"\"(?=.*\\d)(?=.*[a-zA-Z]).*\"\n",[119,1246,1247,1250,1252],{"class":121,"line":650},[119,1248,1249],{"class":207},"    title",[119,1251,211],{"class":125},[119,1253,1254],{"class":214},"\"Include both letters and numbers\"\n",[119,1256,1257],{"class":121,"line":660},[119,1258,1259],{"class":125},"  \u002F>\n",[119,1261,1263,1265,1267,1270,1272],{"class":121,"line":1262},10,[119,1264,138],{"class":125},[119,1266,327],{"class":129},[119,1268,1269],{"class":125},">Sign up\u003C\u002F",[119,1271,327],{"class":129},[119,1273,132],{"class":125},[119,1275,1277,1279,1281],{"class":121,"line":1276},11,[119,1278,167],{"class":125},[119,1280,595],{"class":129},[119,1282,132],{"class":125},[19,1284,1285,1308],{},[22,1286,1287,1290,1291,1290,1294,1290,1297,1290,1300,1303,1304,1307],{},[26,1288,1289],{},"required",", ",[26,1292,1293],{},"minlength",[26,1295,1296],{},"maxlength",[26,1298,1299],{},"min",[26,1301,1302],{},"max",", and ",[26,1305,1306],{},"pattern"," cover the basic checks, and on submit the browser blocks the form and tells the user what's wrong.",[22,1309,1310,1311,1314,1315,1318,1319,1322],{},"For error styles, use CSS ",[26,1312,1313],{},":user-invalid",". Unlike ",[26,1316,1317],{},":invalid",", it only turns red ",[62,1320,1321],{},"after the user has interacted with the field",", so the page doesn't load already covered in red.",[110,1324,1326],{"className":707,"code":1325,"language":709,"meta":115,"style":115},"input:user-invalid {\n  border-color: crimson;\n}\n",[26,1327,1328,1340,1353],{"__ignoreMap":115},[119,1329,1330,1332,1335,1338],{"class":121,"line":122},[119,1331,825],{"class":129},[119,1333,1334],{"class":125},":",[119,1336,1337],{"class":129},"user-invalid",[119,1339,720],{"class":125},[119,1341,1342,1345,1347,1350],{"class":121,"line":135},[119,1343,1344],{"class":725},"  border-color",[119,1346,729],{"class":125},[119,1348,1349],{"class":725},"crimson",[119,1351,1352],{"class":125},";\n",[119,1354,1355],{"class":121,"line":150},[119,1356,757],{"class":125},[759,1358,1359],{},[83,1360,1361],{},[15,1362,1363,1364,94],{},"HTML validation is for user convenience, not security. Anyone can delete the attributes in dev tools, so ",[62,1365,1366],{},"always validate again on the server",[96,1368],{},[10,1370,1372],{"id":1371},"_7-images-performance-from-a-few-attributes","7. Images: performance from a few attributes",[110,1374,1376],{"className":112,"code":1375,"language":114,"meta":115,"style":115},"\u003Cimg\n  src=\"\u002Fimages\u002Fhero-800.jpg\"\n  srcset=\"\u002Fimages\u002Fhero-400.jpg 400w, \u002Fimages\u002Fhero-800.jpg 800w, \u002Fimages\u002Fhero-1600.jpg 1600w\"\n  sizes=\"(max-width: 600px) 100vw, 800px\"\n  width=\"800\"\n  height=\"450\"\n  loading=\"lazy\"\n  decoding=\"async\"\n  alt=\"A developer writing code in front of a laptop\"\n\u002F>\n",[26,1377,1378,1385,1395,1405,1415,1425,1435,1445,1455,1465],{"__ignoreMap":115},[119,1379,1380,1382],{"class":121,"line":122},[119,1381,126],{"class":125},[119,1383,1384],{"class":129},"img\n",[119,1386,1387,1390,1392],{"class":121,"line":135},[119,1388,1389],{"class":207},"  src",[119,1391,211],{"class":125},[119,1393,1394],{"class":214},"\"\u002Fimages\u002Fhero-800.jpg\"\n",[119,1396,1397,1400,1402],{"class":121,"line":150},[119,1398,1399],{"class":207},"  srcset",[119,1401,211],{"class":125},[119,1403,1404],{"class":214},"\"\u002Fimages\u002Fhero-400.jpg 400w, \u002Fimages\u002Fhero-800.jpg 800w, \u002Fimages\u002Fhero-1600.jpg 1600w\"\n",[119,1406,1407,1410,1412],{"class":121,"line":164},[119,1408,1409],{"class":207},"  sizes",[119,1411,211],{"class":125},[119,1413,1414],{"class":214},"\"(max-width: 600px) 100vw, 800px\"\n",[119,1416,1417,1420,1422],{"class":121,"line":391},[119,1418,1419],{"class":207},"  width",[119,1421,211],{"class":125},[119,1423,1424],{"class":214},"\"800\"\n",[119,1426,1427,1430,1432],{"class":121,"line":412},[119,1428,1429],{"class":207},"  height",[119,1431,211],{"class":125},[119,1433,1434],{"class":214},"\"450\"\n",[119,1436,1437,1440,1442],{"class":121,"line":630},[119,1438,1439],{"class":207},"  loading",[119,1441,211],{"class":125},[119,1443,1444],{"class":214},"\"lazy\"\n",[119,1446,1447,1450,1452],{"class":121,"line":650},[119,1448,1449],{"class":207},"  decoding",[119,1451,211],{"class":125},[119,1453,1454],{"class":214},"\"async\"\n",[119,1456,1457,1460,1462],{"class":121,"line":660},[119,1458,1459],{"class":207},"  alt",[119,1461,211],{"class":125},[119,1463,1464],{"class":214},"\"A developer writing code in front of a laptop\"\n",[119,1466,1467],{"class":121,"line":1262},[119,1468,1136],{"class":125},[423,1470,1471,1480],{},[426,1472,1473],{},[429,1474,1475,1477],{},[432,1476,980],{},[432,1478,1479],{},"What it does",[442,1481,1482,1492,1505,1518],{},[429,1483,1484,1489],{},[447,1485,1486],{},[26,1487,1488],{},"loading=\"lazy\"",[447,1490,1491],{},"Delays downloading the image until it's near the viewport (lazy loading)",[429,1493,1494,1502],{},[447,1495,1496,105,1499],{},[26,1497,1498],{},"width",[26,1500,1501],{},"height",[447,1503,1504],{},"Reserves space before the image loads so the page doesn't jump (reduces CLS)",[429,1506,1507,1515],{},[447,1508,1509,105,1512],{},[26,1510,1511],{},"srcset",[26,1513,1514],{},"sizes",[447,1516,1517],{},"Downloads only the resolution that fits the screen",[429,1519,1520,1525],{},[447,1521,1522],{},[26,1523,1524],{},"alt",[447,1526,1527],{},"Alternative text read by screen readers and search engines",[1138,1529,1530],{},[83,1531,1532],{},[15,1533,1534,1537,1538,1540,1541,1544],{},[62,1535,1536],{},"Don't"," put ",[26,1539,1488],{}," on large images visible on first load (hero images). They'll show up later and hurt your LCP (Largest Contentful Paint) score. Those images want ",[26,1542,1543],{},"fetchpriority=\"high\""," instead.",[96,1546],{},[10,1548,1550],{"id":1549},"_8-small-tags-that-carry-meaning","8. Small tags that carry meaning",[15,1552,1553,1554,1556,1557,1560],{},"Visually they're no different from a styled ",[26,1555,119],{},", but they carry ",[62,1558,1559],{},"meaning",", so search engines and assistive tech understand them better.",[423,1562,1563,1576],{},[426,1564,1565],{},[429,1566,1567,1570,1573],{},[432,1568,1569],{},"Tag",[432,1571,1572],{},"Meaning",[432,1574,1575],{},"Example",[442,1577,1578,1597,1615,1632,1647],{},[429,1579,1580,1585,1588],{},[447,1581,1582],{},[26,1583,1584],{},"\u003Ckbd>",[447,1586,1587],{},"Keyboard input",[447,1589,1590,105,1594],{},[1591,1592,1593],"kbd",{},"Ctrl",[1591,1595,1596],{},"C",[429,1598,1599,1604,1607],{},[447,1600,1601],{},[26,1602,1603],{},"\u003Cmark>",[447,1605,1606],{},"Highlight",[447,1608,1609,1610,1614],{},"A ",[1611,1612,1613],"mark",{},"matching word"," in search results",[429,1616,1617,1622,1625],{},[447,1618,1619],{},[26,1620,1621],{},"\u003Cabbr>",[447,1623,1624],{},"Abbreviation (full name on hover)",[447,1626,1627],{},[1628,1629,1631],"abbr",{"title":1630},"Cumulative Layout Shift","CLS",[429,1633,1634,1639,1642],{},[447,1635,1636],{},[26,1637,1638],{},"\u003Ctime>",[447,1640,1641],{},"Machine-readable date",[447,1643,1644],{},[26,1645,1646],{},"\u003Ctime datetime=\"2026-10-08\">Oct 8\u003C\u002Ftime>",[429,1648,1649,1657,1660],{},[447,1650,1651,746,1654],{},[26,1652,1653],{},"\u003Cprogress>",[26,1655,1656],{},"\u003Cmeter>",[447,1658,1659],{},"Progress \u002F measurement",[447,1661,1662,682,1667],{},[1663,1664],"progress",{"value":1665,"max":1666},"70","100",[1668,1669],"meter",{"value":1670,"low":1671,"high":1672},"0.9",0.6,0.8,[15,1674,1675,1676,1678,1679,1681,1682,1684,1685,1688,1689,1692],{},"The keys, the highlight, ",[26,1677,1631],{},", and the two bars in that table are all rendered with the real tags. Hover over ",[26,1680,1631],{},". ",[26,1683,1668],{}," changes color on its own once the value crosses its ",[26,1686,1687],{},"low","\u002F",[26,1690,1691],{},"high"," thresholds (the example is at 90%, so it shows the warning color).",[96,1694],{},[10,1696,1698,1699,1701],{"id":1697},"_9-semantic-tags-to-use-instead-of-div-at-a-glance","9. Semantic tags to use instead of ",[26,1700,28],{},", at a glance",[15,1703,1704],{},"Semantic tags say \"what this region is for\" through the tag name itself. Screen reader users rely on them to jump around (\"skip to main content\"), and search engines use them to pick out the core content more accurately.",[423,1706,1707,1717],{},[426,1708,1709],{},[429,1710,1711,1714],{},[432,1712,1713],{},"If you're writing this",[432,1715,1716],{},"Write this instead",[442,1718,1719,1731,1743,1756,1768,1780,1792],{},[429,1720,1721,1726],{},[447,1722,1723],{},[26,1724,1725],{},"\u003Cdiv class=\"header\">",[447,1727,1728],{},[26,1729,1730],{},"\u003Cheader>",[429,1732,1733,1738],{},[447,1734,1735],{},[26,1736,1737],{},"\u003Cdiv class=\"nav\">",[447,1739,1740],{},[26,1741,1742],{},"\u003Cnav>",[429,1744,1745,1750],{},[447,1746,1747],{},[26,1748,1749],{},"\u003Cdiv class=\"main\">",[447,1751,1752,1755],{},[26,1753,1754],{},"\u003Cmain>"," (one per page)",[429,1757,1758,1763],{},[447,1759,1760],{},[26,1761,1762],{},"\u003Cdiv class=\"post\">",[447,1764,1765],{},[26,1766,1767],{},"\u003Carticle>",[429,1769,1770,1775],{},[447,1771,1772],{},[26,1773,1774],{},"\u003Cdiv class=\"sidebar\">",[447,1776,1777],{},[26,1778,1779],{},"\u003Caside>",[429,1781,1782,1787],{},[447,1783,1784],{},[26,1785,1786],{},"\u003Cdiv class=\"footer\">",[447,1788,1789],{},[26,1790,1791],{},"\u003Cfooter>",[429,1793,1794,1799],{},[447,1795,1796],{},[26,1797,1798],{},"\u003Cdiv onclick=\"...\">",[447,1800,1801],{},[26,1802,1803],{},"\u003Cbutton>",[15,1805,1806,1807,1809,1810,1813,1814,1816,1817,1819],{},"The last row matters most. A ",[26,1808,28],{}," with a click handler ",[62,1811,1812],{},"can't be pressed from the keyboard",", and screen readers don't know it's a button. Things you click are ",[26,1815,327],{},"s; things that take you to another page are ",[26,1818,373],{},"s. Following just that rule removes a large share of accessibility problems.",[96,1821],{},[10,1823,1825],{"id":1824},"summary-why-this-is-worth-knowing","Summary: why this is worth knowing",[423,1827,1828,1838],{},[426,1829,1830],{},[429,1831,1832,1835],{},[432,1833,1834],{},"What you want to build",[432,1836,1837],{},"The HTML answer",[442,1839,1840,1855,1866,1877,1886,1895,1908],{},[429,1841,1842,1845],{},[447,1843,1844],{},"FAQ accordion",[447,1846,1847,105,1849,1851,1852,1854],{},[26,1848,104],{},[26,1850,108],{}," (+ ",[26,1853,188],{},")",[429,1856,1857,1860],{},[447,1858,1859],{},"Dropdown \u002F tooltip",[447,1861,1862,105,1864],{},[26,1863,308],{},[26,1865,454],{},[429,1867,1868,1871],{},[447,1869,1870],{},"Confirmation modal",[447,1872,1873,105,1875],{},[26,1874,508],{},[26,1876,674],{},[429,1878,1879,1882],{},[447,1880,1881],{},"Search suggestions",[447,1883,1884],{},[26,1885,788],{},[429,1887,1888,1891],{},[447,1889,1890],{},"Number keypad on mobile",[447,1892,1893],{},[26,1894,1025],{},[429,1896,1897,1900],{},[447,1898,1899],{},"Input validation",[447,1901,1902,1290,1904,105,1906],{},[26,1903,1289],{},[26,1905,1306],{},[26,1907,1313],{},[429,1909,1910,1913],{},[447,1911,1912],{},"Image performance",[447,1914,1915,1290,1917,1688,1919,1290,1921],{},[26,1916,1488],{},[26,1918,1498],{},[26,1920,1501],{},[26,1922,1511],{},[15,1924,1925],{},"Using what the browser gives you gets you three things at once:",[1927,1928,1929,1935,1941],"ol",{},[22,1930,1931,1934],{},[62,1932,1933],{},"Less code."," You can delete state management, event handling, and outside-click detection.",[22,1936,1937,1940],{},[62,1938,1939],{},"Accessibility comes along."," The browser already handles keyboard use, focus management, and screen reader announcements.",[22,1942,1943,1946],{},[62,1944,1945],{},"A lighter bundle."," That's one less UI library to install.",[15,1948,1949,1950,1953],{},"Of course, if you need very fine-grained styling or complex behavior, building it yourself or reaching for a library is the right call. But just building the habit of ",[62,1951,1952],{},"checking \"does HTML already have this?\" before you decide"," will make your projects much lighter.",[1955,1956,1957],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":115,"searchDepth":135,"depth":135,"links":1959},[1960,1961,1963,1965,1967,1969,1971,1972,1973,1974,1976],{"id":12,"depth":135,"text":13},{"id":100,"depth":135,"text":1962},"1. Accordion: \u003Cdetails> + \u003Csummary>",{"id":304,"depth":135,"text":1964},"2. Popovers: the popover attribute",{"id":504,"depth":135,"text":1966},"3. Modals: \u003Cdialog>",{"id":784,"depth":135,"text":1968},"4. Autocomplete: \u003Cdatalist>",{"id":959,"depth":135,"text":1970},"5. \u003Cinput> attributes that take care of mobile keyboards",{"id":1159,"depth":135,"text":1160},{"id":1371,"depth":135,"text":1372},{"id":1549,"depth":135,"text":1550},{"id":1697,"depth":135,"text":1975},"9. Semantic tags to use instead of div, at a glance",{"id":1824,"depth":135,"text":1825},"Accordions, popovers, modals, autocomplete, form validation, and lazy-loaded images. A guide to the features you get from plain HTML tags and attributes, no library required. Less code, and accessibility comes for free.","md",{"date":1980,"field":1981,"tags":1982},"2026.10.08","frontend",[114],"\u002Fen\u002Fhtml-features-without-javascript",{"title":5,"description":1977},{"loc":1983},"en\u002Fhtml-features-without-javascript","TPqHiukyHTlUsSTTBaZyvCiOHqmAJsfGE4y3KvuotBw",1791459767455]