<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>CSS on Jingyuan</title>
        <link>https://jingyuan-zheng.github.io/tags/css/</link>
        <description>Recent content in CSS on Jingyuan</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-US</language>
        <copyright>Jingyuan Zheng</copyright>
        <lastBuildDate>Fri, 24 Jul 2026 20:00:00 +0200</lastBuildDate><atom:link href="https://jingyuan-zheng.github.io/tags/css/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>An Apple-Style Web Clock Widget: Design Notes and Technical Details</title>
            <link>https://jingyuan-zheng.github.io/p/apple-style-web-clock-widget/</link>
            <pubDate>Fri, 24 Jul 2026 20:00:00 +0200</pubDate>
            <guid>https://jingyuan-zheng.github.io/p/apple-style-web-clock-widget/</guid>
            <description>&lt;img src=&#34;https://jingyuan-zheng.github.io/img/apple-style-web-clock-widget/featured.png&#34; alt=&#34;Featured image of post An Apple-Style Web Clock Widget: Design Notes and Technical Details&#34; /&gt;&lt;p&gt;This post explains how I designed and built the new Apple-style dual-time web clock on my homepage, from its visual decisions to the time-zone and rendering logic behind it.&lt;/p&gt;&#xA;&lt;p&gt;The clock now sits directly below the search box on the desktop version of the &lt;a class=&#34;link&#34; href=&#34;https://jingyuan-zheng.github.io/&#34; &gt;homepage&lt;/a&gt;. It compares the visitor&amp;rsquo;s current time with “My time” using two analog dials, while deliberately avoiding a widget title, a personal name, or a country label.&lt;/p&gt;&#xA;&lt;p&gt;It is a small addition, but it brought together more interesting problems than its size suggests: browser time zones, daylight-saving changes, analog geometry, light and dark presentation, responsive layout, bilingual text, and accessible live information.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;A light daytime clock and a dark nighttime clock inside an Apple-style web widget&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://jingyuan-zheng.github.io/img/apple-style-web-clock-widget/featured.png&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;The reusable source code is now available on GitHub:&#xA;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Jingyuan-Zheng/apple-style-web-clock-widget&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Jingyuan-Zheng/apple-style-web-clock-widget&lt;/a&gt;.&#xA;The MIT-licensed repository includes a standalone demo, framework-free CSS and JavaScript, bilingual documentation, configuration examples, and tests for time-zone and hand-angle calculations.&lt;/p&gt;&#xA;&lt;div class=&#34;clock-demo-embed&#34;&gt;&#xA;    &lt;iframe&#xA;        src=&#34;https://jingyuan-zheng.github.io/apple-style-web-clock-widget/?embed=1&#34;&#xA;        title=&#34;Interactive Apple-style web clock demo&#34;&#xA;        loading=&#34;lazy&#34;&#xA;    &gt;&lt;/iframe&gt;&#xA;&lt;/div&gt;&#xA;&lt;h2 id=&#34;a-familiar-interface-without-copying-an-app&#34;&gt;A Familiar Interface Without Copying an App&#xA;&lt;/h2&gt;&lt;p&gt;The visual direction is clearly inspired by Apple&amp;rsquo;s clock interfaces: circular faces, thin hands, restrained typography, a bright orange second hand, and a rounded card with soft depth.&lt;/p&gt;&#xA;&lt;p&gt;The goal was not to reproduce a specific Apple screen pixel for pixel. It was to borrow the qualities that make those interfaces immediately understandable:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;the clock should be recognizable before any text is read;&lt;/li&gt;&#xA;&lt;li&gt;the two time zones should feel related but remain visually distinct;&lt;/li&gt;&#xA;&lt;li&gt;labels should explain only what the user needs;&lt;/li&gt;&#xA;&lt;li&gt;decoration should never compete with the hands or numerals.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;That is why the visible copy is reduced to &lt;strong&gt;“Your time”&lt;/strong&gt; and &lt;strong&gt;“My time.”&lt;/strong&gt; A heading such as “World Clock” would repeat what the two dials already communicate, while a personal name or country would add visual noise and expose more context than the widget needs.&lt;/p&gt;&#xA;&lt;h2 id=&#34;one-instant-two-wall-clocks&#34;&gt;One Instant, Two Wall Clocks&#xA;&lt;/h2&gt;&lt;p&gt;The most important technical decision is to treat both dials as two views of the same instant.&lt;/p&gt;&#xA;&lt;p&gt;Every update starts from one &lt;code&gt;Date&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;now&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;The visitor&amp;rsquo;s zone comes from the browser:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;localZone&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;Intl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;DateTimeFormat&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;resolvedOptions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;timeZone&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;UTC&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;The second dial uses the time zone configured by the site. Both are passed through &lt;code&gt;Intl.DateTimeFormat&lt;/code&gt; with &lt;code&gt;formatToParts()&lt;/code&gt;, which gives numeric year, month, day, hour, minute, and second values for the selected IANA time zone.&lt;/p&gt;&#xA;&lt;p&gt;This is safer than manually adding a fixed number of hours. A fixed &lt;code&gt;+1&lt;/code&gt; or &lt;code&gt;+2&lt;/code&gt; eventually becomes wrong when daylight-saving rules change. The &lt;code&gt;Intl&lt;/code&gt; API lets the browser&amp;rsquo;s time-zone database resolve the applicable offset for the current instant.&lt;/p&gt;&#xA;&lt;h2 id=&#34;calculating-the-time-difference&#34;&gt;Calculating the Time Difference&#xA;&lt;/h2&gt;&lt;p&gt;Displaying a wall-clock time is only half the job. The widget also shows the difference between the two zones.&lt;/p&gt;&#xA;&lt;p&gt;For a target zone, the formatted wall-clock parts are reconstructed as if they were UTC:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;wallTimeAsUtc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UTC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;year&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;month&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;day&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;hour&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;minute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nx&#34;&gt;second&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;offsetMinutes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;round&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;wallTimeAsUtc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;currentSecond&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;60000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;The difference displayed under the second clock is then:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;targetOffsetMinutes&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;localOffsetMinutes&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;This also handles half-hour and quarter-hour zones. The output formatter keeps one decimal place when necessary instead of assuming that every time-zone difference is a whole number.&lt;/p&gt;&#xA;&lt;h2 id=&#34;turning-time-into-hand-angles&#34;&gt;Turning Time into Hand Angles&#xA;&lt;/h2&gt;&lt;p&gt;An analog clock is mostly geometry. A complete circle is 360 degrees, so each minute or second represents 6 degrees, and each hour represents 30 degrees.&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;minuteAngle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;minute&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;hourAngle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;hour&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;%&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;30&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;minuteAngle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;secondAngle&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;second&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;The small &lt;code&gt;minuteAngle / 12&lt;/code&gt; term matters. Without it, the hour hand would jump abruptly from one number to the next. Including it makes the hour hand travel gradually as the minutes pass.&lt;/p&gt;&#xA;&lt;p&gt;Each hand is a narrow positioned element whose transform origin sits at its bottom center:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;transform&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;rotate&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;var&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;--clock-rotation&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;transform-origin&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;bottom&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;center&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;JavaScript only updates the custom property. CSS remains responsible for the hand&amp;rsquo;s shape, length, color, and positioning.&lt;/p&gt;&#xA;&lt;p&gt;The numerals use the same separation of concerns. Hugo generates twelve repeated elements, CSS rotates each one around the dial, and an inverse rotation keeps the numeral upright.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-site-theme-and-the-dial-theme-are-different&#34;&gt;The Site Theme and the Dial Theme Are Different&#xA;&lt;/h2&gt;&lt;p&gt;The component has two independent kinds of appearance.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;strong&gt;outer card&lt;/strong&gt; follows the blog&amp;rsquo;s light or dark theme so that it belongs naturally in the sidebar. The &lt;strong&gt;clock face&lt;/strong&gt;, however, follows the time shown by that particular clock:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;07:00–18:59 uses a light dial;&lt;/li&gt;&#xA;&lt;li&gt;19:00–06:59 uses a dark dial.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;This distinction was important. If the entire widget followed only the website theme, switching the blog to dark mode would turn a daytime clock dark as well. That would remove useful information from the visual design.&lt;/p&gt;&#xA;&lt;p&gt;Because each dial evaluates its own zoned hour, it is possible—and intentional—for one face to be light while the other is dark.&lt;/p&gt;&#xA;&lt;h2 id=&#34;recalculate-instead-of-counting&#34;&gt;Recalculate Instead of Counting&#xA;&lt;/h2&gt;&lt;p&gt;The widget refreshes once per second with &lt;code&gt;setInterval&lt;/code&gt;, but it never treats the previous tick as the source of truth.&lt;/p&gt;&#xA;&lt;p&gt;Each tick creates a fresh &lt;code&gt;Date&lt;/code&gt; and recalculates every hand from the current system time. If the browser delays a timer because a tab is in the background, the clock corrects itself on the next update instead of accumulating drift.&lt;/p&gt;&#xA;&lt;p&gt;This also means daylight-saving changes and system time adjustments are picked up naturally. The interval is only a request to repaint; it is not the clock&amp;rsquo;s internal timekeeper.&lt;/p&gt;&#xA;&lt;h2 id=&#34;hugo-integration-and-the-desktop-only-decision&#34;&gt;Hugo Integration and the Desktop-Only Decision&#xA;&lt;/h2&gt;&lt;p&gt;The widget is implemented as a local Hugo partial and registered immediately after the search widget in the homepage configuration. This keeps the structure modular: the search component remains unchanged, and the clock can be moved or removed through the widget list.&lt;/p&gt;&#xA;&lt;p&gt;The HTML contains the semantic structure and bilingual labels. SCSS owns the card, dial, hands, numerals, and theme states. TypeScript owns time-zone conversion and live updates.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;a class=&#34;link&#34; href=&#34;https://github.com/Jingyuan-Zheng/apple-style-web-clock-widget&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;open-source version&lt;/a&gt; removes the Hugo dependency and exposes the same ideas through standard &lt;code&gt;data-*&lt;/code&gt; attributes, so it can be embedded in an ordinary webpage without a framework or build step.&lt;/p&gt;&#xA;&lt;p&gt;The blog&amp;rsquo;s right sidebar appears only at desktop widths of 1024 pixels and above, so the clock does not consume space on phones. This is an intentional product decision rather than an unfinished responsive state. On a small screen, the article list and navigation are more important than a decorative time comparison.&lt;/p&gt;&#xA;&lt;h2 id=&#34;small-accessibility-and-localization-details&#34;&gt;Small Accessibility and Localization Details&#xA;&lt;/h2&gt;&lt;p&gt;Animated clock hands are not enough for assistive technology. Each dial therefore receives an updated accessible label containing its name, digital time, and time difference. The visible difference line is hidden from screen readers to avoid announcing the same information twice.&lt;/p&gt;&#xA;&lt;p&gt;The labels and hour units come from the current Hugo language:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;English: “Your time,” “My time,” “hour,” and “hours”;&lt;/li&gt;&#xA;&lt;li&gt;Simplified Chinese: “你的时间,” “我的时间,” and “小时.”&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Time calculations deliberately use Latin digits and a Gregorian calendar internally. Presentation text remains localized without making the numeric parsing dependent on a browser&amp;rsquo;s display language.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-this-small-widget-taught-me&#34;&gt;What This Small Widget Taught Me&#xA;&lt;/h2&gt;&lt;p&gt;The main lesson was that a good widget needs boundaries more than it needs features.&lt;/p&gt;&#xA;&lt;p&gt;I removed the title, personal name, country label, and mobile version. I separated website theming from day-and-night dial logic. I let the browser handle time-zone rules, while keeping the analog drawing in simple CSS and geometry.&lt;/p&gt;&#xA;&lt;p&gt;The result is intentionally narrow: two clocks, one comparison, and no controls. That restraint is what lets it feel like part of the homepage instead of a miniature app competing with the rest of the site.&lt;/p&gt;&#xA;&lt;p&gt;Small interface components are often where design and engineering meet most directly. Every extra label, timer, color rule, and breakpoint has an immediate visual consequence. Building this clock was a useful reminder that polish usually comes not from adding more, but from deciding what the component can leave out.&lt;/p&gt;&#xA;</description>
        </item><item>
            <title>Why Chinese Text Falls Back to a Serif Font in Safari</title>
            <link>https://jingyuan-zheng.github.io/p/safari-chinese-font-songti-fallback-fix/</link>
            <pubDate>Wed, 22 Jul 2026 11:00:00 +0200</pubDate>
            <guid>https://jingyuan-zheng.github.io/p/safari-chinese-font-songti-fallback-fix/</guid>
            <description>&lt;img src=&#34;https://jingyuan-zheng.github.io/img/safari-cjk-font-fallback-fix/featured.png&#34; alt=&#34;Featured image of post Why Chinese Text Falls Back to a Serif Font in Safari&#34; /&gt;&lt;p&gt;This post explains a common Safari typography problem for developers and designers: a page is intended to use a clean sans-serif style, but some Chinese text unexpectedly appears in a serif font.&lt;/p&gt;&#xA;&lt;p&gt;To readers who use Chinese every day, the change is immediately visible. It is roughly comparable to a navigation label suddenly switching from a modern sans-serif face to Times New Roman. The text remains readable, but it no longer belongs to the surrounding interface.&lt;/p&gt;&#xA;&lt;div class=&#34;post-figure&#34;&gt;&#xA;    &lt;img src=&#34;https://jingyuan-zheng.github.io/img/safari-cjk-font-fallback-fix/fig-pingfang.webp&#34; alt=&#34;A Bilibili page in Safari with Chinese text correctly rendered in PingFang&#34;&gt;&#xA;    &lt;div class=&#34;caption&#34;&gt;Figure 1: The intended result—the Chinese interface text uses the sans-serif PingFang family.&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;div class=&#34;post-figure&#34;&gt;&#xA;    &lt;img src=&#34;https://jingyuan-zheng.github.io/img/safari-cjk-font-fallback-fix/fig-songti.webp&#34; alt=&#34;The same Bilibili page in Safari with some Chinese text unexpectedly rendered in Songti&#34;&gt;&#xA;    &lt;div class=&#34;caption&#34;&gt;Figure 2: The fallback problem—parts of the same interface unexpectedly use the serif Songti family.&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;h2 id=&#34;if-you-read-chinese-and-just-want-to-fix-safari&#34;&gt;If You Read Chinese and Just Want to Fix Safari&#xA;&lt;/h2&gt;&lt;p&gt;You do not need to understand the CSS or change any website code. The project provides one ready-to-use stylesheet:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Jingyuan-Zheng/Safari-CJK-Font-Fallback-Fix/blob/main/safari-fix.css&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Download the complete &lt;code&gt;safari-fix.css&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Open Safari Settings and select &lt;strong&gt;Advanced&lt;/strong&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Choose the downloaded file from the &lt;strong&gt;Style sheet&lt;/strong&gt; menu.&lt;/li&gt;&#xA;&lt;li&gt;Quit Safari completely with &lt;kbd&gt;Command&lt;/kbd&gt;+&lt;kbd&gt;Q&lt;/kbd&gt;, then reopen it.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;If you later want to disable the fix, return to the same menu and select &lt;strong&gt;None Selected&lt;/strong&gt;. It does not install a font, modify macOS, or require a Safari extension.&lt;/p&gt;&#xA;&lt;div class=&#34;post-figure&#34;&gt;&#xA;    &lt;img src=&#34;https://jingyuan-zheng.github.io/img/safari-cjk-font-fallback-fix/fig-safari-stylesheet.webp&#34; alt=&#34;Safari Advanced settings with safari-fix.css selected in the Style sheet menu&#34;&gt;&#xA;    &lt;div class=&#34;caption&#34;&gt;Figure 3: Select safari-fix.css from Safari Settings → Advanced → Style sheet.&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;The rest of this article explains the underlying problem and how developers can prevent it on their own websites.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-short-explanation&#34;&gt;The Short Explanation&#xA;&lt;/h2&gt;&lt;p&gt;Many sites begin their font stack with Arial, Helvetica, Inter, Roboto, or another Latin typeface. These fonts can render English letters and numbers, but they may not contain Chinese characters.&lt;/p&gt;&#xA;&lt;p&gt;When the browser reaches a character that the selected font cannot draw, it must find another font. This is font fallback. If the remaining stack does not name a suitable Chinese sans-serif font, Safari may choose a Chinese serif font instead.&lt;/p&gt;&#xA;&lt;p&gt;That is why a line can contain perfectly normal English alongside Chinese text that looks as though it came from a different design system. Chrome and Safari may also choose different fallback fonts for the same CSS, so testing only one browser can hide the problem.&lt;/p&gt;&#xA;&lt;h2 id=&#34;start-with-a-better-font-stack&#34;&gt;Start with a Better Font Stack&#xA;&lt;/h2&gt;&lt;p&gt;If the design is meant to be sans-serif, say so for both Latin and Chinese text. A practical stack for a multilingual interface might look like this:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nt&#34;&gt;body&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;font-family&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;Inter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;apple-system&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;BlinkMacSystemFont&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;PingFang SC&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;Hiragino Sans GB&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;s2&#34;&gt;&amp;#34;Microsoft YaHei&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;kc&#34;&gt;sans-serif&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;The exact order depends on the product and supported platforms, but the intention should be clear:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;keep the preferred Latin or brand typeface first;&lt;/li&gt;&#xA;&lt;li&gt;include an appropriate Chinese sans-serif family;&lt;/li&gt;&#xA;&lt;li&gt;include a suitable Windows alternative when Windows is supported;&lt;/li&gt;&#xA;&lt;li&gt;end with the generic &lt;code&gt;sans-serif&lt;/code&gt; family.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Avoid placing &lt;code&gt;SimSun&lt;/code&gt;, &lt;code&gt;Songti SC&lt;/code&gt;, or another Chinese serif face inside a stack that is otherwise intended to be sans-serif. Those fonts can be excellent choices for editorial reading, literature, or a deliberately serif design. They are simply the wrong fallback for a sans-serif interface.&lt;/p&gt;&#xA;&lt;p&gt;If the design intentionally uses a Chinese serif font, declare that choice explicitly rather than relying on whichever fallback the browser happens to find.&lt;/p&gt;&#xA;&lt;h2 id=&#34;do-not-solve-it-by-overriding-every-font&#34;&gt;Do Not Solve It by Overriding Every Font&#xA;&lt;/h2&gt;&lt;p&gt;A frequently shared workaround applies PingFang to every element:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;font-family&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;PingFang SC&amp;#34;&lt;/span&gt; &lt;span class=&#34;cp&#34;&gt;!important&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;This removes the visible serif fallback, but it also removes much of the site&amp;rsquo;s typography.&lt;/p&gt;&#xA;&lt;p&gt;It can replace icon fonts, change monospace code, alter Latin headings, override component-specific styles, and erase intentional serif typography. In other words, it fixes one fallback decision by taking all font decisions away from the page.&lt;/p&gt;&#xA;&lt;p&gt;A safer solution should change only the missing Chinese glyphs while leaving Latin text, numbers, icons, code, and deliberate serif choices alone.&lt;/p&gt;&#xA;&lt;h2 id=&#34;a-targeted-fallback-rule&#34;&gt;A Targeted Fallback Rule&#xA;&lt;/h2&gt;&lt;p&gt;The core technique used by this project is an &lt;code&gt;@font-face&lt;/code&gt; rule with &lt;code&gt;unicode-range&lt;/code&gt;. The following shortened example tells Safari to use PingFang only for the listed Chinese character ranges when a page requests Arial:&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;@&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;font-face&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;font-family&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Arial&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;src&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;local&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;PingFangSC-Regular&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;unicode-range&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;U&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;3000-303F&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;U&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;3400-4DBF&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;nt&#34;&gt;U&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;4E00-9FFF&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;U&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;F900-FAFF&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;U&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;FF00-FFEF&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;Latin characters still come from Arial. Chinese characters that Arial cannot provide come from PingFang. The page therefore keeps its existing Latin metrics and visual identity while gaining a predictable Chinese sans-serif fallback.&lt;/p&gt;&#xA;&lt;p&gt;The complete project also covers bold text, Traditional Chinese, extended character ranges, common Latin font names, and Windows Chinese font names. The snippet above is only an explanation, not the full stylesheet.&lt;/p&gt;&#xA;&lt;p&gt;You can review or download the maintained file here:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Jingyuan-Zheng/Safari-CJK-Font-Fallback-Fix/blob/main/safari-fix.css&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Download the complete &lt;code&gt;safari-fix.css&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Jingyuan-Zheng/Safari-CJK-Font-Fallback-Fix&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;View Safari CJK Font Fallback Fix on GitHub&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;If the project fixes the problem for you, consider giving it a &lt;strong&gt;Star on GitHub&lt;/strong&gt;. It helps other Safari users and web developers find the solution.&lt;/p&gt;&#xA;&lt;h2 id=&#34;a-user-fix-and-a-website-fix-are-different&#34;&gt;A User Fix and a Website Fix Are Different&#xA;&lt;/h2&gt;&lt;p&gt;When you do not control the affected websites, the downloadable Safari stylesheet is a practical compatibility layer. For a site you maintain, correcting the site&amp;rsquo;s own font stack is preferable because every visitor benefits without installing anything.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-designers-should-check&#34;&gt;What Designers Should Check&#xA;&lt;/h2&gt;&lt;p&gt;CJK typography should be part of design review, not left entirely to the browser.&lt;/p&gt;&#xA;&lt;p&gt;A useful review does not require every team member to read Chinese. Check the visible form of the text:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Does Chinese body text use a serif or sans-serif style that matches the design?&lt;/li&gt;&#xA;&lt;li&gt;Do navigation labels and buttons look consistent with nearby Latin text?&lt;/li&gt;&#xA;&lt;li&gt;Do regular and bold Chinese text have a clear but stable weight difference?&lt;/li&gt;&#xA;&lt;li&gt;Are punctuation, brackets, and full-width symbols visually consistent?&lt;/li&gt;&#xA;&lt;li&gt;Does the design still work when a single line mixes Chinese, English, and numbers?&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Chinese characters are visually dense, so differences in weight and spacing can be more obvious than expected. A Latin-only mock-up is not enough to validate a multilingual interface.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-developers-should-test&#34;&gt;What Developers Should Test&#xA;&lt;/h2&gt;&lt;p&gt;Use actual Chinese content in browser testing. A few translated menu labels are more useful than placeholder boxes or repeated sample characters.&lt;/p&gt;&#xA;&lt;p&gt;At minimum, test:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Safari and Chrome on macOS;&lt;/li&gt;&#xA;&lt;li&gt;normal and bold text;&lt;/li&gt;&#xA;&lt;li&gt;headings, navigation, buttons, form controls, and code blocks;&lt;/li&gt;&#xA;&lt;li&gt;Simplified and Traditional Chinese when both are supported;&lt;/li&gt;&#xA;&lt;li&gt;mixed Chinese, English, numbers, and punctuation;&lt;/li&gt;&#xA;&lt;li&gt;the production build after web fonts finish loading.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;In browser developer tools, inspect the font that actually renders the Chinese glyphs, not only the computed &lt;code&gt;font-family&lt;/code&gt; declaration. The computed stack may say Arial or Inter even though a different local font supplied the Chinese characters.&lt;/p&gt;&#xA;&lt;p&gt;Also test web-font failure. A blocked, slow, or partially loaded font should fall back to a typeface from the same broad category rather than unexpectedly switching from sans-serif to serif.&lt;/p&gt;&#xA;&lt;h2 id=&#34;privacy-and-limitations&#34;&gt;Privacy and Limitations&#xA;&lt;/h2&gt;&lt;p&gt;The project is CSS only. It contains no JavaScript, analytics, telemetry, or access to browsing history.&lt;/p&gt;&#xA;&lt;p&gt;As a Safari user stylesheet, it cannot reliably change text drawn into images or canvas elements, and some embedded or isolated components may not inherit its rules. No generic stylesheet can perfectly infer whether every serif choice was intentional, which is another reason the website&amp;rsquo;s own font stack remains the best place to solve the problem.&lt;/p&gt;&#xA;&lt;h2 id=&#34;the-broader-lesson&#34;&gt;The Broader Lesson&#xA;&lt;/h2&gt;&lt;p&gt;&lt;code&gt;sans-serif&lt;/code&gt; is a design category, not a guarantee that every writing system will receive the expected local font. A multilingual font stack should describe the intended result for the scripts the product actually supports.&lt;/p&gt;&#xA;&lt;p&gt;For Chinese interfaces, the essential principle is simple: pair a Latin sans-serif design with an explicit Chinese sans-serif fallback, preserve deliberate serif typography, and test the rendered fonts in the browsers your users rely on.&lt;/p&gt;&#xA;&lt;h2 id=&#34;references&#34;&gt;References&#xA;&lt;/h2&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://support.apple.com/guide/safari/ibrw1075/mac&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Apple: Change Advanced settings in Safari on Mac&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://bugs.webkit.org/show_bug.cgi?id=10874&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;WebKit Bug 10874: CJK font selection and language information&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/Jingyuan-Zheng/Safari-CJK-Font-Fallback-Fix&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Safari CJK Font Fallback Fix on GitHub&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;</description>
        </item></channel>
</rss>
