[{"data":1,"prerenderedAt":1149},["ShallowReactive",2],{"\u002Fposts\u002Fobserve-css-vars":3,"surround:\u002Fposts\u002Fobserve-css-vars":1140},{"id":4,"title":5,"body":6,"createdAt":1133,"description":16,"extension":1134,"meta":1135,"navigation":23,"path":1136,"seo":1137,"stem":1138,"__hash__":1139},"posts\u002Fposts\u002F2024\u002Fobserve-css-vars.md","如何实现监听 CSS 变量？",{"type":7,"value":8,"toc":1124},"minimark",[9,13,17,24,27,32,35,40,48,115,118,122,129,228,231,235,238,253,256,259,293,311,351,354,461,464,514,531,537,560,566,577,659,662,938,942,945,985,997,1004,1065,1071,1104,1107,1111,1121],[10,11,5],"h1",{"id":12},"如何实现监听-css-变量",[14,15,16],"p",{},"先来看下这个效果（把 demo 写出来之后才发现效果还挺炸裂的）：",[14,18,19],{},[20,21],"video",{"src":22,"controls":23},"https:\u002F\u002Fuser-images.githubusercontent.com\u002F5101076\u002Fd8f8ec89-b82c-4770-b001-5f4b1bb03bb6",true,[14,25,26],{},"这相当于将 CSS 变量与 JS 彻底打通了。传统意义上我们似乎只能通过 JS 控制 CSS，但是看起来实际上也是可以反过来的。",[28,29,31],"h2",{"id":30},"how","怎么做到的？",[14,33,34],{},"这个问题乍一想可能不难，但再想想可能也没那么简单。",[36,37,39],"h3",{"id":38},"how-1","方案一？",[14,41,42,43,47],{},"有的同学可能知道 DOM 有一个 ",[44,45,46],"code",{},"getComputedStyle","，它会返回一个“实时的” CSSStyleDeclaration。也就意味着：",[49,50,55],"pre",{"className":51,"code":52,"language":53,"meta":54,"style":54},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const style = getComputedStyle(myAnchor)\nstyle.textDecorationLine \u002F\u002F none\n\u002F\u002F hover anchor\nstyle.textDecorationLine \u002F\u002F underline\n","js","",[44,56,57,81,97,103],{"__ignoreMap":54},[58,59,62,66,70,74,78],"span",{"class":60,"line":61},"line",1,[58,63,65],{"class":64},"spNyl","const",[58,67,69],{"class":68},"sTEyZ"," style ",[58,71,73],{"class":72},"sMK4o","=",[58,75,77],{"class":76},"s2Zo4"," getComputedStyle",[58,79,80],{"class":68},"(myAnchor)\n",[58,82,84,87,90,93],{"class":60,"line":83},2,[58,85,86],{"class":68},"style",[58,88,89],{"class":72},".",[58,91,92],{"class":68},"textDecorationLine ",[58,94,96],{"class":95},"sHwdD","\u002F\u002F none\n",[58,98,100],{"class":60,"line":99},3,[58,101,102],{"class":95},"\u002F\u002F hover anchor\n",[58,104,106,108,110,112],{"class":60,"line":105},4,[58,107,86],{"class":68},[58,109,89],{"class":72},[58,111,92],{"class":68},[58,113,114],{"class":95},"\u002F\u002F underline\n",[14,116,117],{},"然而，基于一个实时的 DOM 对象，我们只能实现“在访问它的时候获取最新值”，而不能实现“在它更新时触发某个回调”。",[36,119,121],{"id":120},"how-2","方案二？",[14,123,124,125,128],{},"对 DOM 更加熟悉的同学可能会想到 ",[44,126,127],{},"MutationObserver","，它允许我们监听 DOM 树上的几乎任意变化：",[49,130,132],{"className":51,"code":131,"language":53,"meta":54,"style":54},"new MutationObserver(() => {\n  console.log('element added or removed')\n}).observe(document, { childList: true, subtree: true });\n",[44,133,134,154,179],{"__ignoreMap":54},[58,135,136,139,142,145,148,151],{"class":60,"line":61},[58,137,138],{"class":72},"new",[58,140,141],{"class":76}," MutationObserver",[58,143,144],{"class":68},"(",[58,146,147],{"class":72},"()",[58,149,150],{"class":64}," =>",[58,152,153],{"class":72}," {\n",[58,155,156,159,161,164,167,170,174,176],{"class":60,"line":83},[58,157,158],{"class":68},"  console",[58,160,89],{"class":72},[58,162,163],{"class":76},"log",[58,165,144],{"class":166},"swJcz",[58,168,169],{"class":72},"'",[58,171,173],{"class":172},"sfazB","element added or removed",[58,175,169],{"class":72},[58,177,178],{"class":166},")\n",[58,180,181,184,187,189,192,195,198,201,204,207,211,213,216,218,220,223,225],{"class":60,"line":99},[58,182,183],{"class":72},"}",[58,185,186],{"class":68},")",[58,188,89],{"class":72},[58,190,191],{"class":76},"observe",[58,193,194],{"class":68},"(document",[58,196,197],{"class":72},",",[58,199,200],{"class":72}," {",[58,202,203],{"class":166}," childList",[58,205,206],{"class":72},":",[58,208,210],{"class":209},"sfNiH"," true",[58,212,197],{"class":72},[58,214,215],{"class":166}," subtree",[58,217,206],{"class":72},[58,219,210],{"class":209},[58,221,222],{"class":72}," }",[58,224,186],{"class":68},[58,226,227],{"class":72},";\n",[14,229,230],{},"然而，CSS 变化并不一定，甚至通常都不是 DOM 变化引起的。典型的例子就是伪类\u002F伪元素的改变：按钮或者链接 hover 状态下有不同的样式，此时 DOM 树并未更新，显然 MutationObserver 也无法实现监听。",[36,232,234],{"id":233},"how-3","方案三？",[14,236,237],{},"清明堵车的时候我想到了一个答案：",[14,239,240,241,244,245,248,249,252],{},"有一组 DOM 事件和 CSS 息息相关，那就是 ",[44,242,243],{},"transition*"," 和 ",[44,246,247],{},"animation*","。例如最常见的：在 CSS 过渡结束后，元素的 ",[44,250,251],{},"transitionend"," 事件会被触发。我们有没有可能利用这个特性来实现监听 CSS 属性呢？",[14,254,255],{},"答案是可以！",[14,257,258],{},"首先我们需要知道一些事实：",[260,261,262,283],"ul",{},[263,264,265,266,269,270,273,274,277,278,273,280],"li",{},"过渡必须在一个 CSS 合法的范围内进行，例如 ",[44,267,268],{},"color"," 可以从 ",[44,271,272],{},"red"," 过渡到 ",[44,275,276],{},"blue","，但是不能从 ",[44,279,272],{},[44,281,282],{},"1px",[263,284,285,286,288,289,292],{},"想要触发 ",[44,287,243],{}," 事件，过渡必须是发生过的，意味着 ",[44,290,291],{},"transition-duration"," 必须大于 0",[14,294,295,296,299,300,303,304,307,308],{},"然后我们就可以寻找一些合适的方案啦！以演示的例子为例，我们可以将 ",[44,297,298],{},"--my-integer"," 映射到任意一个语法是 ",[44,301,302],{},"\u003Cinteger>"," 的 CSS 属性上，比如 ",[44,305,306],{},"z-index"," 或者 ",[44,309,310],{},"order",[49,312,316],{"className":313,"code":314,"language":315,"meta":54,"style":54},"language-css shiki shiki-themes material-theme-lighter material-theme material-theme-palenight",".custom-integer {\n  z-index: var(--my-integer);\n}\n","css",[44,317,318,328,346],{"__ignoreMap":54},[58,319,320,322,326],{"class":60,"line":61},[58,321,89],{"class":72},[58,323,325],{"class":324},"sBMFI","custom-integer",[58,327,153],{"class":72},[58,329,330,334,336,339,341,343],{"class":60,"line":83},[58,331,333],{"class":332},"sqsOY","  z-index",[58,335,206],{"class":72},[58,337,338],{"class":76}," var",[58,340,144],{"class":72},[58,342,298],{"class":68},[58,344,345],{"class":72},");\n",[58,347,348],{"class":60,"line":99},[58,349,350],{"class":72},"}\n",[14,352,353],{},"这里有一个问题是，这可能会影响元素本身的样式。所以一个更好的方案是，我们可以构造一个伪元素来完成：",[49,355,357],{"className":313,"code":356,"language":315,"meta":54,"style":54},".custom-integer::before {\n  content: '';\n  position: absolute;\n  width: 0;\n  height: 0;\n  visibility: hidden;\n\n  z-index: var(--my-integer);\n}\n",[44,358,359,373,385,397,410,422,435,441,456],{"__ignoreMap":54},[58,360,361,363,365,368,371],{"class":60,"line":61},[58,362,89],{"class":72},[58,364,325],{"class":324},[58,366,367],{"class":72},"::",[58,369,370],{"class":64},"before",[58,372,153],{"class":72},[58,374,375,378,380,383],{"class":60,"line":83},[58,376,377],{"class":332},"  content",[58,379,206],{"class":72},[58,381,382],{"class":72}," ''",[58,384,227],{"class":72},[58,386,387,390,392,395],{"class":60,"line":99},[58,388,389],{"class":332},"  position",[58,391,206],{"class":72},[58,393,394],{"class":68}," absolute",[58,396,227],{"class":72},[58,398,399,402,404,408],{"class":60,"line":105},[58,400,401],{"class":332},"  width",[58,403,206],{"class":72},[58,405,407],{"class":406},"sbssI"," 0",[58,409,227],{"class":72},[58,411,413,416,418,420],{"class":60,"line":412},5,[58,414,415],{"class":332},"  height",[58,417,206],{"class":72},[58,419,407],{"class":406},[58,421,227],{"class":72},[58,423,425,428,430,433],{"class":60,"line":424},6,[58,426,427],{"class":332},"  visibility",[58,429,206],{"class":72},[58,431,432],{"class":68}," hidden",[58,434,227],{"class":72},[58,436,438],{"class":60,"line":437},7,[58,439,440],{"emptyLinePlaceholder":23},"\n",[58,442,444,446,448,450,452,454],{"class":60,"line":443},8,[58,445,333],{"class":332},[58,447,206],{"class":72},[58,449,338],{"class":76},[58,451,144],{"class":72},[58,453,298],{"class":68},[58,455,345],{"class":72},[58,457,459],{"class":60,"line":458},9,[58,460,350],{"class":72},[14,462,463],{},"然后，我们需要在元素上构造一个有时间的过渡。如果有多个值也是可以的：",[49,465,467],{"className":313,"code":466,"language":315,"meta":54,"style":54},".custom-integer::before {\n  z-index: var(--my-integer);\n  transition: z-index 1ms;\n}\n",[44,468,469,481,495,510],{"__ignoreMap":54},[58,470,471,473,475,477,479],{"class":60,"line":61},[58,472,89],{"class":72},[58,474,325],{"class":324},[58,476,367],{"class":72},[58,478,370],{"class":64},[58,480,153],{"class":72},[58,482,483,485,487,489,491,493],{"class":60,"line":83},[58,484,333],{"class":332},[58,486,206],{"class":72},[58,488,338],{"class":76},[58,490,144],{"class":72},[58,492,298],{"class":68},[58,494,345],{"class":72},[58,496,497,500,502,505,508],{"class":60,"line":99},[58,498,499],{"class":332},"  transition",[58,501,206],{"class":72},[58,503,504],{"class":68}," z-index ",[58,506,507],{"class":406},"1ms",[58,509,227],{"class":72},[58,511,512],{"class":60,"line":105},[58,513,350],{"class":72},[14,515,516,517,520,521,244,524,527,528,530],{},"CSS 中 ",[44,518,519],{},"\u003Ctime>"," 仅支持 ",[44,522,523],{},"s",[44,525,526],{},"ms"," 两个单位。考虑到浏览器的帧率一般不会高于 120 FPS 这个数量级，设置为 ",[44,529,507],{}," 已经足够了。",[14,532,533,534,536],{},"另外在事件上我们也有更好的选择。",[44,535,243],{}," 有几个不同的事件：",[260,538,539,545,555],{},[263,540,541,544],{},[44,542,543],{},"transitionrun"," 在过渡开始时触发，也就是选择器一旦作用（例如 button 被 hover）就会触发。",[263,546,547,550,551,554],{},[44,548,549],{},"transitionstart"," 在过渡开始进行时触发，也就是样式一旦生效就会触发。主要的区别在于，此时 ",[44,552,553],{},"transition-delay"," 的时间已经过去了。",[263,556,557,559],{},[44,558,251],{}," 在过渡结束后触发。",[14,561,562,563,565],{},"根据上面的信息，我们使用 ",[44,564,543],{}," 将是更好的选择。",[14,567,568,569,572,573,576],{},"在 DOM 中，",[44,570,571],{},"TransitionEvent"," 具有 ",[44,574,575],{},"pseudoElement"," 属性，使用它我们可以避免元素本身对这个机制的影响：",[49,578,580],{"className":51,"code":579,"language":53,"meta":54,"style":54},"element.addEventListener('transitionrun', event => {\n  if (event.pseudoElement === '::before') {\n    \u002F\u002F ...\n  }\n})\n",[44,581,582,610,643,648,653],{"__ignoreMap":54},[58,583,584,587,589,592,594,596,598,600,602,606,608],{"class":60,"line":61},[58,585,586],{"class":68},"element",[58,588,89],{"class":72},[58,590,591],{"class":76},"addEventListener",[58,593,144],{"class":68},[58,595,169],{"class":72},[58,597,543],{"class":172},[58,599,169],{"class":72},[58,601,197],{"class":72},[58,603,605],{"class":604},"sHdIc"," event",[58,607,150],{"class":64},[58,609,153],{"class":72},[58,611,612,616,619,622,624,626,629,632,635,637,640],{"class":60,"line":83},[58,613,615],{"class":614},"s7zQu","  if",[58,617,618],{"class":166}," (",[58,620,621],{"class":68},"event",[58,623,89],{"class":72},[58,625,575],{"class":68},[58,627,628],{"class":72}," ===",[58,630,631],{"class":72}," '",[58,633,634],{"class":172},"::before",[58,636,169],{"class":72},[58,638,639],{"class":166},") ",[58,641,642],{"class":72},"{\n",[58,644,645],{"class":60,"line":99},[58,646,647],{"class":95},"    \u002F\u002F ...\n",[58,649,650],{"class":60,"line":105},[58,651,652],{"class":72},"  }\n",[58,654,655,657],{"class":60,"line":412},[58,656,183],{"class":72},[58,658,178],{"class":68},[14,660,661],{},"有了这些，我们就完全能实现上面的效果了！当然对于 Vue，我们也可以包装一些更实用的 Composition API 出来，比方说：",[49,663,665],{"className":51,"code":664,"language":53,"meta":54,"style":54},"function useTransitionListener(pseudoElement) {\n  const timestamp = ref()\n  const listener = function (event) {\n    if (typeof pseudoElement === 'string' && event.pseudoElement !== pseudoElement) return\n    timestamp.value = event.timeStamp\n  }\n  return { timestamp, listener }\n}\n\nconst { timestamp, listener } = useTransitionListener('::before')\n\nconst myInteger = computed(() => {\n  void timestamp \u002F\u002F 这里触发依赖收集\n  return parseInt(getComputedStyle(elementRef.value).getPropertyValue('--my-integer'), 10)\n})\n\n\u002F\u002F 接下来只要绑定 listener 给 elementRef，并设置好样式，就能实现良好的效果咯\n",[44,666,667,683,700,720,761,780,784,800,804,808,838,843,864,875,920,927,932],{"__ignoreMap":54},[58,668,669,672,675,677,679,681],{"class":60,"line":61},[58,670,671],{"class":64},"function",[58,673,674],{"class":76}," useTransitionListener",[58,676,144],{"class":72},[58,678,575],{"class":604},[58,680,186],{"class":72},[58,682,153],{"class":72},[58,684,685,688,691,694,697],{"class":60,"line":83},[58,686,687],{"class":64},"  const",[58,689,690],{"class":68}," timestamp",[58,692,693],{"class":72}," =",[58,695,696],{"class":76}," ref",[58,698,699],{"class":166},"()\n",[58,701,702,704,707,709,712,714,716,718],{"class":60,"line":99},[58,703,687],{"class":64},[58,705,706],{"class":68}," listener",[58,708,693],{"class":72},[58,710,711],{"class":64}," function",[58,713,618],{"class":72},[58,715,621],{"class":604},[58,717,186],{"class":72},[58,719,153],{"class":72},[58,721,722,725,727,730,733,735,737,740,742,745,747,749,751,754,756,758],{"class":60,"line":105},[58,723,724],{"class":614},"    if",[58,726,618],{"class":166},[58,728,729],{"class":72},"typeof",[58,731,732],{"class":68}," pseudoElement",[58,734,628],{"class":72},[58,736,631],{"class":72},[58,738,739],{"class":172},"string",[58,741,169],{"class":72},[58,743,744],{"class":72}," &&",[58,746,605],{"class":68},[58,748,89],{"class":72},[58,750,575],{"class":68},[58,752,753],{"class":72}," !==",[58,755,732],{"class":68},[58,757,639],{"class":166},[58,759,760],{"class":614},"return\n",[58,762,763,766,768,771,773,775,777],{"class":60,"line":412},[58,764,765],{"class":68},"    timestamp",[58,767,89],{"class":72},[58,769,770],{"class":68},"value",[58,772,693],{"class":72},[58,774,605],{"class":68},[58,776,89],{"class":72},[58,778,779],{"class":68},"timeStamp\n",[58,781,782],{"class":60,"line":424},[58,783,652],{"class":72},[58,785,786,789,791,793,795,797],{"class":60,"line":437},[58,787,788],{"class":614},"  return",[58,790,200],{"class":72},[58,792,690],{"class":68},[58,794,197],{"class":72},[58,796,706],{"class":68},[58,798,799],{"class":72}," }\n",[58,801,802],{"class":60,"line":443},[58,803,350],{"class":72},[58,805,806],{"class":60,"line":458},[58,807,440],{"emptyLinePlaceholder":23},[58,809,811,813,815,817,819,822,824,826,828,830,832,834,836],{"class":60,"line":810},10,[58,812,65],{"class":64},[58,814,200],{"class":72},[58,816,690],{"class":68},[58,818,197],{"class":72},[58,820,821],{"class":68}," listener ",[58,823,183],{"class":72},[58,825,693],{"class":72},[58,827,674],{"class":76},[58,829,144],{"class":68},[58,831,169],{"class":72},[58,833,634],{"class":172},[58,835,169],{"class":72},[58,837,178],{"class":68},[58,839,841],{"class":60,"line":840},11,[58,842,440],{"emptyLinePlaceholder":23},[58,844,846,848,851,853,856,858,860,862],{"class":60,"line":845},12,[58,847,65],{"class":64},[58,849,850],{"class":68}," myInteger ",[58,852,73],{"class":72},[58,854,855],{"class":76}," computed",[58,857,144],{"class":68},[58,859,147],{"class":72},[58,861,150],{"class":64},[58,863,153],{"class":72},[58,865,867,870,872],{"class":60,"line":866},13,[58,868,869],{"class":72},"  void",[58,871,690],{"class":68},[58,873,874],{"class":95}," \u002F\u002F 这里触发依赖收集\n",[58,876,878,880,883,885,887,889,892,894,896,898,900,903,905,907,909,911,913,915,918],{"class":60,"line":877},14,[58,879,788],{"class":614},[58,881,882],{"class":76}," parseInt",[58,884,144],{"class":166},[58,886,46],{"class":76},[58,888,144],{"class":166},[58,890,891],{"class":68},"elementRef",[58,893,89],{"class":72},[58,895,770],{"class":68},[58,897,186],{"class":166},[58,899,89],{"class":72},[58,901,902],{"class":76},"getPropertyValue",[58,904,144],{"class":166},[58,906,169],{"class":72},[58,908,298],{"class":172},[58,910,169],{"class":72},[58,912,186],{"class":166},[58,914,197],{"class":72},[58,916,917],{"class":406}," 10",[58,919,178],{"class":166},[58,921,923,925],{"class":60,"line":922},15,[58,924,183],{"class":72},[58,926,178],{"class":68},[58,928,930],{"class":60,"line":929},16,[58,931,440],{"emptyLinePlaceholder":23},[58,933,935],{"class":60,"line":934},17,[58,936,937],{"class":95},"\u002F\u002F 接下来只要绑定 listener 给 elementRef，并设置好样式，就能实现良好的效果咯\n",[36,939,941],{"id":940},"how-4","方案四？",[14,943,944],{},"上面的方案有什么问题呢？问题在于，如果我有一个 CSS 语法合法，但无法对应给 CSS 现有属性的值就崩了。比如：",[49,946,948],{"className":313,"code":947,"language":315,"meta":54,"style":54},".custom-list {\n  --my-list: 1px 0 1px 0 1px 0;\n}\n",[44,949,950,959,981],{"__ignoreMap":54},[58,951,952,954,957],{"class":60,"line":61},[58,953,89],{"class":72},[58,955,956],{"class":324},"custom-list",[58,958,153],{"class":72},[58,960,961,964,966,969,971,973,975,977,979],{"class":60,"line":83},[58,962,963],{"class":68},"  --my-list",[58,965,206],{"class":72},[58,967,968],{"class":406}," 1px",[58,970,407],{"class":406},[58,972,968],{"class":406},[58,974,407],{"class":406},[58,976,968],{"class":406},[58,978,407],{"class":406},[58,980,227],{"class":72},[58,982,983],{"class":60,"line":99},[58,984,350],{"class":72},[14,986,987,988,992,993,996],{},"不好意思，CSS 没有属性支持",[989,990,991],"strong",{},"以空格分隔的超过四个的长度","，并且 CSS 也无法通过 ",[44,994,995],{},"calc()"," 等方法操作列表。这样要怎么办呢？",[14,998,999,1000,1003],{},"正在实施的 CSS Houdini 帮了我们一个大忙！在 Chromium 中，我们可以通过 ",[44,1001,1002],{},"@property"," “定义”一个 CSS 属性，比方说：",[49,1005,1007],{"className":313,"code":1006,"language":315,"meta":54,"style":54},"@property --my-list {\n  syntax: '\u003Clength>+ | auto';\n  inherits: true;\n  initial-value: 'auto';\n}\n",[44,1008,1009,1018,1034,1045,1061],{"__ignoreMap":54},[58,1010,1011,1013,1016],{"class":60,"line":61},[58,1012,1002],{"class":614},[58,1014,1015],{"class":68}," --my-list ",[58,1017,642],{"class":72},[58,1019,1020,1023,1025,1027,1030,1032],{"class":60,"line":83},[58,1021,1022],{"class":68},"  syntax",[58,1024,206],{"class":72},[58,1026,631],{"class":72},[58,1028,1029],{"class":172},"\u003Clength>+ | auto",[58,1031,169],{"class":72},[58,1033,227],{"class":72},[58,1035,1036,1039,1041,1043],{"class":60,"line":99},[58,1037,1038],{"class":68},"  inherits",[58,1040,206],{"class":72},[58,1042,210],{"class":68},[58,1044,227],{"class":72},[58,1046,1047,1050,1052,1054,1057,1059],{"class":60,"line":105},[58,1048,1049],{"class":332},"  initial-value",[58,1051,206],{"class":72},[58,1053,631],{"class":72},[58,1055,1056],{"class":172},"auto",[58,1058,169],{"class":72},[58,1060,227],{"class":72},[58,1062,1063],{"class":60,"line":412},[58,1064,350],{"class":72},[14,1066,1067,1068,1070],{},"这样我们就定义了一个支持“任意长度空格分隔的长度值，或者是 ",[44,1069,1056],{},"，并且会继承父元素” 的属性了。然后，我们就可以直接",[49,1072,1074],{"className":313,"code":1073,"language":315,"meta":54,"style":54},".custom-integer::before {\n  transition: --my-list 1ms;\n}\n",[44,1075,1076,1088,1100],{"__ignoreMap":54},[58,1077,1078,1080,1082,1084,1086],{"class":60,"line":61},[58,1079,89],{"class":72},[58,1081,325],{"class":324},[58,1083,367],{"class":72},[58,1085,370],{"class":64},[58,1087,153],{"class":72},[58,1089,1090,1092,1094,1096,1098],{"class":60,"line":83},[58,1091,499],{"class":332},[58,1093,206],{"class":72},[58,1095,1015],{"class":68},[58,1097,507],{"class":406},[58,1099,227],{"class":72},[58,1101,1102],{"class":60,"line":99},[58,1103,350],{"class":72},[14,1105,1106],{},"这样就一切完美！不过还是需要注意，上述用法仍然处于试验中，且只有 Chromium 支持。",[28,1108,1110],{"id":1109},"application","应用场景",[14,1112,1113,1114,1120],{},"说了这么多，有什么用呢？可以看下 ",[1115,1116,1117],"a",{"href":1117,"rel":1118},"https:\u002F\u002Froughness.vercel.app\u002F",[1119],"nofollow"," 关于样式的支持。当样式改变时，SVG\u002FCanvas 可以自动重新绘制，也许这就是未来自定义元素的工作方式。",[86,1122,1123],{},"html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sqsOY, html code.shiki .sqsOY{--shiki-light:#8796B0;--shiki-default:#B2CCD6;--shiki-dark:#B2CCD6}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}",{"title":54,"searchDepth":83,"depth":83,"links":1125},[1126,1132],{"id":30,"depth":83,"text":31,"children":1127},[1128,1129,1130,1131],{"id":38,"depth":99,"text":39},{"id":120,"depth":99,"text":121},{"id":233,"depth":99,"text":234},{"id":940,"depth":99,"text":941},{"id":1109,"depth":83,"text":1110},"2024-04-07","md",{},"\u002Fposts\u002Fobserve-css-vars",{"title":5,"description":16},"posts\u002F2024\u002Fobserve-css-vars","bQ5ePFMCQdO7j_mFZLWsbhJL8EfCsPSUoikaZl-IGBc",[1141,1145],{"title":1142,"path":1143,"stem":1144,"children":-1},"为了保护隐私，Web 标准牺牲了什么？","\u002Fposts\u002Fweb-features-and-privacy","posts\u002F2023\u002Fweb-features-and-privacy",{"title":1146,"path":1147,"stem":1148,"children":-1},"为什么我不推荐你使用“结构化样式”","\u002Fposts\u002Fwhy-not-use-structured-styles","posts\u002F2025\u002Fwhy-not-use-structured-styles",1792500630763]