{"id":10151,"date":"2020-04-16T10:11:23","date_gmt":"2020-04-16T08:11:23","guid":{"rendered":"https:\/\/phrase.com\/blog\/?p=10151"},"modified":"2024-02-13T16:22:10","modified_gmt":"2024-02-13T15:22:10","slug":"how-do-i-use-a-css-file-for-site-localization","status":"publish","type":"post","link":"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/","title":{"rendered":"CSS Localization"},"content":{"rendered":"\n<div id=\"acf\/text-block_1a7e1d7a49b201a0b3120c25ace59d4a\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>In the ever-evolving landscape of web development, CSS has grown by leaps and bounds, transforming the way we design, build, and interact with web content. Yet one of its most powerful capabilities remains largely underutilized: CSS localization.<\/p>\n<p>This guide aims to remedy this, covering the ins and outs of CSS localization. We start with essential HTML attributes for language-specific styling and directional text \u2014 including right-to-left layouts, and vertical text presentations for East Asian scripts. Logical properties are the perfect complement for these vertical and right-to-left layouts, and we cover those too.<\/p>\n<p>We hope to offer designers and developers some hidden gems of CSS that can make your pages more inclusive, and available to a global audience without too much effort.<\/p>\n<p>\ud83d\udca1 Internationalization (i18n) and localization (l10n) allow us to make our apps available in different languages and to different regions, often for more profit. If you\u2019re new to i18n and l10n, check out our guide to <a href=\"https:\/\/phrase.com\/blog\/posts\/i18n-a-simple-definition\/\">internationalization<\/a>.<\/p>\n<p>\ud83d\udd17\u00a0You can interact and play with many of the concepts we cover here via our <a href=\"https:\/\/stackblitz.com\/~\/github.com\/PhraseApp-Blog\/css-localization\">live demo on StackBlitz<\/a>. You can also <a href=\"https:\/\/github.com\/PhraseApp-Blog\/css-localization\">get all the demo code from GitHub<\/a>.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_69_1 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Overview<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#the-lang-attribute\" title=\"The lang attribute\">The lang attribute<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#locale-specificity\" title=\"Locale specificity\">Locale specificity<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#direction-right-to-left-layouts\" title=\"Direction (right-to-left layouts)\">Direction (right-to-left layouts)<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#writing-mode-vertical-layouts\" title=\"Writing mode (vertical layouts)\">Writing mode (vertical layouts)<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#logical-properties\" title=\"Logical properties\">Logical properties<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#a-note-on-inline-and-block\" title=\"A note on inline and block\">A note on inline and block<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#sizing\" title=\"Sizing\">Sizing<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#positioning\" title=\"Positioning\">Positioning<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#spacing\" title=\"Spacing\">Spacing<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#other-logical-properties\" title=\"Other logical properties\">Other logical properties<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#fonts\" title=\"Fonts\">Fonts<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#system-fonts\" title=\"System fonts\">System fonts<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#multilingual-fonts\" title=\"Multilingual fonts\">Multilingual fonts<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#google-noto\" title=\"Google Noto\">Google Noto<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#font-fallback\" title=\"Font fallback\">Font fallback<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#pronunciation-marks\" title=\"Pronunciation marks\">Pronunciation marks<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#ruby\" title=\"Ruby\">Ruby<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#text-emphasis\" title=\"Text emphasis\">Text emphasis<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-19\" href=\"https:\/\/phrase.com\/blog\/posts\/how-do-i-use-a-css-file-for-site-localization\/#the-joy-of-css\" title=\"The joy of CSS\">The joy of CSS<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"the-lang-attribute\"><\/span>The lang attribute<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Setting the <code>lang<\/code> attribute is the most essential thing we can do when localizing our web pages. <code>lang<\/code> is a <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Global_attributes\">global attribute<\/a>, meaning that it\u2019s available on all HTML tags. Since we normally use a single language on a given page, the most common use of <code>lang<\/code> is setting it on the <code>&lt;html&gt;<\/code> document element.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-meta\">&lt;!DOCTYPE <span class=\"hljs-meta-keyword\">html<\/span>&gt;<\/span>\n<span class=\"hljs-comment\">&lt;!-- \ud83d\udc47 Set the language of the\n        document to English. --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en\"<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">charset<\/span>=<span class=\"hljs-string\">\"UTF-8\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"viewport\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">\"width=device-width, initial-scale=1.0\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">title<\/span>&gt;<\/span>An English Page<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">title<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">head<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>&gt;<\/span>A English Page<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n  <span class=\"hljs-comment\">&lt;!-- ... --&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">html<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_f915c650d3c1519aa9e98e2bd5b2c917\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\uddd2\ufe0f\u00a0When setting the <code>lang<\/code> attribute on HTML elements, we typically use an <a href=\"https:\/\/en.wikipedia.org\/wiki\/IETF_language_tag\">IETF BCP 47 language tag<\/a>. BCP 47 tags begin with a language code \u2014 <code>en<\/code> for English, <code>fr<\/code> for French, <code>es<\/code> for Spanish \u2014 and have optional region specifiers (more on regions a bit later).<\/p>\n<p>\ud83d\udd17 Check out the <a href=\"https:\/\/en.wikipedia.org\/wiki\/List_of_ISO_639-1_codes\">list of language tags on Wikipedia<\/a>.<\/p>\n<p>In the above example, we set the entire document to English by using the language tag, <code>en<\/code>.<\/p>\n<p><!-- notionvc: 2c009d3a-c8c6-4269-8983-29686ed6f087 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en\"<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_fb3d3b40f019df621a67167268f710bc\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Setting the <code>lang<\/code> attribute makes using proper pronunciation easier for screen readers and other assistive technologies. It also lets search engines know which language our page is in, which helps with SEO (Search Engine Optimization).<\/p>\n<p>\ud83d\udd17 <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Global_attributes\/lang\">Read more about the lang attribute on MDN<\/a>.<\/p>\n<p>We can set <code>lang<\/code> on almost any HTML element. For example, we might have a Korean paragraph in the middle of our otherwise English page.<\/p>\n<p><!-- notionvc: 5caba01e-e9c9-4474-9db5-2dade8745fa3 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en\"<\/span>&gt;<\/span>\n  <span class=\"hljs-comment\">&lt;!-- ... --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>An English paragraph.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Another English paragraph.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"ko\"<\/span>&gt;<\/span>\ud55c\uad6d\uc5b4 \ubb38\ub2e8\uc785\ub2c8\ub2e4.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">html<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_d26418786383887248ad486012a91f23\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We can target these elements using the CSS <a class=\"notion-link-token notion-focusable-token notion-enable-hover\" tabindex=\"0\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/:lang\" rel=\"noopener noreferrer\" data-token-index=\"1\"><span class=\"link-annotation-unknown-block-id-642245174\">:lang() pseudo-class<\/span><\/a>. <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"3\">:lang(en)<\/span>, for example, will target any element that has <span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"5\">lang=&#8221;en&#8221;<\/span>. <!-- notionvc: d51ee5da-f1a0-4711-8a51-8b42071320a9 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-comment\">\/* \n * Global selector \u2014 selects any element \n * with lang=\"en\" \n *\/<\/span>\n<span class=\"hljs-selector-pseudo\">:lang(en)<\/span> {\n  <span class=\"hljs-attribute\">color<\/span>: deepskyblue;\n}\n\n<span class=\"hljs-comment\">\/* \n * Element selector \u2014 selects all paragraphs\n * with lang=\"ko\"\n *\/<\/span>\n<span class=\"hljs-selector-tag\">p<\/span><span class=\"hljs-selector-pseudo\">:lang(ko)<\/span> {\n  <span class=\"hljs-attribute\">color<\/span>: deeppink;\n  <span class=\"hljs-attribute\">border<\/span>: <span class=\"hljs-number\">1px<\/span> solid deeppink;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_9974c4461badb8b1de2e470fac433a2f\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\uddd2\ufe0f\u00a0The <code>lang<\/code> attribute doesn\u2019t need to be set directly on the element. If the element inherits <code>lang=\"en\"<\/code> from an ancestor in the DOM, <code>:lang(en)<\/code> will target it.<\/p>\n<figure id=\"attachment_75070\" aria-describedby=\"caption-attachment-75070\" style=\"width: 764px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-75070\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/lang-attr.png\" alt=\"Our global :lang() selector targets all elements on the page but is overridden by the p:lang() attribute.\" width=\"764\" height=\"266\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/lang-attr.png 764w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/lang-attr-300x104.png 300w\" sizes=\"(max-width: 764px) 100vw, 764px\" \/><figcaption id=\"caption-attachment-75070\" class=\"wp-caption-text\">Our global :lang() selector targets all elements on the page but is overridden by the p:lang() attribute.<\/figcaption><\/figure>\n<h3><span class=\"ez-toc-section\" id=\"locale-specificity\"><\/span>Locale specificity<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>We mentioned earlier that <code>lang<\/code> values are typically BCP 47 tags, which can be a language tag like <code>en<\/code> or <code>ko<\/code>. BCP 47 tags can be further specified by adding a region code according to the ISO Alpha-2 standard (like <code>BH<\/code> for Bahrain, <code>CN<\/code> for China, <code>US<\/code> for the United States). So a qualified locale identifier might appear as <code>en-US<\/code> for American English or <code>zh-CN<\/code> for Chinese as used in China.<\/p>\n<p>\ud83d\udd17\u00a0For country codes, the ISO&#8217;s <a href=\"https:\/\/www.iso.org\/obp\/ui\/#search\">search tool<\/a> is a good resource.<\/p>\n<p>When targeting elements with <code>:lang()<\/code>, general locales will match more specific locales. For example, <code>:lang(en)<\/code> will match elements with <code>lang=\"en\"<\/code> and elements with <code>lang=\"en-US\"<\/code>.<\/p>\n<p><!-- notionvc: 77f43ee0-7bb3-42f8-b44a-613600fb48c8 --><\/p>\n<p><!-- notionvc: bc29db15-c4a2-492d-bd89-1ab3398cefb8 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en\"<\/span>&gt;<\/span>Some English text.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en-US\"<\/span>&gt;<\/span>Some American English text.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en-GB\"<\/span>&gt;<\/span>Some British English text.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"en-CA\"<\/span>&gt;<\/span>Some Canadian English text.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">p<\/span><span class=\"hljs-selector-pseudo\">:lang(en)<\/span> {\n  <span class=\"hljs-attribute\">color<\/span>: darkslategray;\n  <span class=\"hljs-attribute\">border-bottom<\/span>: <span class=\"hljs-number\">1px<\/span> solid <span class=\"hljs-number\">#ccc<\/span>;\n}\n\n<span class=\"hljs-selector-tag\">p<\/span><span class=\"hljs-selector-pseudo\">:lang(en-CA)<\/span> {\n  <span class=\"hljs-comment\">\/* Canadian flag red *\/<\/span>\n  <span class=\"hljs-attribute\">color<\/span>: <span class=\"hljs-number\">#d80621<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_3e9f5bfcf4f842c5139852c9b69d1a0b\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<figure id=\"attachment_75077\" aria-describedby=\"caption-attachment-75077\" style=\"width: 750px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-75077\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/lang-specificty.png\" alt=\"The :lang(en) selector targets specific English locales, such as en-US, en-GB, and en-CA.\" width=\"750\" height=\"330\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/lang-specificty.png 750w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/lang-specificty-300x132.png 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><figcaption id=\"caption-attachment-75077\" class=\"wp-caption-text\">The :lang(en) selector targets specific English locales, such as en-US, en-GB, and en-CA.<\/figcaption><\/figure>\n<h2><!-- notionvc: f3cfbe99-3bba-4710-9f95-324c6ee663c8 --><\/h2>\n<h2><span class=\"ez-toc-section\" id=\"direction-right-to-left-layouts\"><\/span>Direction (right-to-left layouts)<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Languages like Arabic, Hebrew, and Urdu are read right-to-left (<code>rtl<\/code>). The <code>dir<\/code> HTML attribute handles these languages wonderfully. <code>dir<\/code> can have a value of <code>ltr<\/code> (left-to-right), <code>rtl<\/code> (right-to-left), or <code>auto<\/code> (based on content).<\/p>\n<p>Again, since a web page is often in one language, and hence one direction, the most common use case for <code>dir<\/code> is setting on the <code>html<\/code> document element.<\/p>\n<p><!-- notionvc: c3fe0c91-ee55-4d03-b319-c3c2d9e7d68d --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"ar\"<\/span> <span class=\"hljs-attr\">dir<\/span>=<span class=\"hljs-string\">\"rtl\"<\/span>&gt;<\/span>\n<span class=\"hljs-comment\">&lt;!-- ... --&gt;<\/span> <\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_7c82d6f799524eb7f967c883d456586d\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>All modern browsers will flow the page in the direction specified by <code>dir<\/code>.<\/p>\n<figure id=\"attachment_75085\" aria-describedby=\"caption-attachment-75085\" style=\"width: 600px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-75085\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/dir-switching.gif\" alt=\"Firefox reflowing the page according to the html element\u2019s dir attribute.\" width=\"600\" height=\"351\" \/><figcaption id=\"caption-attachment-75085\" class=\"wp-caption-text\">Firefox reflowing the page according to the html element\u2019s dir attribute.<\/figcaption><\/figure>\n<p>\ud83d\udd17\u00a0<a href=\"https:\/\/stackblitz.com\/~\/github.com\/PhraseApp-Blog\/css-localization\">Try the above demo yourself on StackBlitz<\/a>. You can also <a href=\"https:\/\/github.com\/PhraseApp-Blog\/css-localization\">get all demo code from GitHub<\/a>.<\/p>\n<p>Just like <code>lang<\/code>, <code>dir<\/code> is a global attribute and can be applied to any HTML element, not just the document root.<\/p>\n<p><!-- notionvc: 5dc8418a-f4ab-4aa5-82a6-ed79c89c5526 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">dir<\/span>=<span class=\"hljs-string\">\"ltr\"<\/span>&gt;<\/span>\n  But I must explain to you how all this mistaken idea of\n  denouncing pleasure and praising pain was born.\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n\n\/* Should be equivalent to dir=\"ltr\" *\/\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">dir<\/span>=<span class=\"hljs-string\">\"auto\"<\/span>&gt;<\/span>\n  But I must explain to you how all this mistaken idea of\n  denouncing pleasure and praising pain was born.\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">dir<\/span>=<span class=\"hljs-string\">\"rtl\"<\/span>&gt;<\/span>\n  \u0644\u0643\u0646 \u0644\u0627 \u0628\u062f \u0623\u0646 \u0623\u0648\u0636\u062d \u0644\u0643 \u0623\u0646 \u0643\u0644 \u0647\u0630\u0647 \u0627\u0644\u0623\u0641\u0643\u0627\u0631 \u0627\u0644\u0645\u063a\u0644\u0648\u0637\u0629 \u062d\u0648\u0644\n  \u0627\u0633\u062a\u0646\u0643\u0627\u0631 \u0627\u0644\u0646\u0634\u0648\u0629 \u0648\u062a\u0645\u062c\u064a\u062f \u0627\u0644\u0623\u0644\u0645 \u0646\u0634\u0623\u062a \u0628\u0627\u0644\u0641\u0639\u0644\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n\n\/* Should be equivalent to dir=\"rtl\" *\/\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">dir<\/span>=<span class=\"hljs-string\">\"auto\"<\/span>&gt;<\/span>\n  \u0644\u0643\u0646 \u0644\u0627 \u0628\u062f \u0623\u0646 \u0623\u0648\u0636\u062d \u0644\u0643 \u0623\u0646 \u0643\u0644 \u0647\u0630\u0647 \u0627\u0644\u0623\u0641\u0643\u0627\u0631 \u0627\u0644\u0645\u063a\u0644\u0648\u0637\u0629 \u062d\u0648\u0644\n  \u0627\u0633\u062a\u0646\u0643\u0627\u0631 \u0627\u0644\u0646\u0634\u0648\u0629 \u0648\u062a\u0645\u062c\u064a\u062f \u0627\u0644\u0623\u0644\u0645 \u0646\u0634\u0623\u062a \u0628\u0627\u0644\u0641\u0639\u0644\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_9db24c0c4c4ccdd5d889a464a004fea8\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<figure id=\"attachment_75091\" aria-describedby=\"caption-attachment-75091\" style=\"width: 806px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-75091\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/browser-dir.png\" alt=\"When dir=\u201dauto\u201d, the browser will use the first character with a strong directionality and use that for the direction of the element.\" width=\"806\" height=\"656\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/browser-dir.png 806w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/browser-dir-300x244.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/browser-dir-768x625.png 768w\" sizes=\"(max-width: 806px) 100vw, 806px\" \/><figcaption id=\"caption-attachment-75091\" class=\"wp-caption-text\">When dir=\u201dauto\u201d, the browser will use the first character with a strong directionality and use that for the direction of the element.<\/figcaption><\/figure>\n<p>In CSS, we can target <code>rtl<\/code> and <code>ltr<\/code> elements with normal attribute selectors.<\/p>\n<p><!-- notionvc: 2ffc5077-e3b3-45f8-b6d7-af56e7ae1a7e --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-attr\">&#91;dir=<span class=\"hljs-string\">\"ltr\"<\/span>]<\/span> {\n  <span class=\"hljs-attribute\">padding-left<\/span>: <span class=\"hljs-number\">1rem<\/span>;\n  <span class=\"hljs-attribute\">border-left<\/span>: <span class=\"hljs-number\">2px<\/span> solid deepskyblue;\n}\n\n<span class=\"hljs-selector-attr\">&#91;dir=<span class=\"hljs-string\">\"rtl\"<\/span>]<\/span> {\n  <span class=\"hljs-attribute\">padding-right<\/span>: <span class=\"hljs-number\">1rem<\/span>;\n  <span class=\"hljs-attribute\">border-right<\/span>: <span class=\"hljs-number\">2px<\/span> solid deepskyblue;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_9318184e005aa2ff3b2655a01f7b7e98\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<figure id=\"attachment_75103\" aria-describedby=\"caption-attachment-75103\" style=\"width: 1042px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-75103 size-full\" title=\"Note that with dir=\u201dltr\u201d and dir=\u201drtl\u201d we only target those explicitly set directions, but not dir=\u201dauto\u201d\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/css-dir-targeting.png\" alt=\"Note that with dir=\u201dltr\u201d and dir=\u201drtl\u201d we only target those explicitly set directions, but not dir=\u201dauto\u201d\" width=\"1042\" height=\"570\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/css-dir-targeting.png 1042w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/css-dir-targeting-300x164.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/css-dir-targeting-1024x560.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/css-dir-targeting-768x420.png 768w\" sizes=\"(max-width: 1042px) 100vw, 1042px\" \/><figcaption id=\"caption-attachment-75103\" class=\"wp-caption-text\">Note that with dir=\u201dltr\u201d and dir=\u201drtl\u201d we only target those explicitly set directions, but not dir=\u201dauto\u201d<\/figcaption><\/figure>\n<p>The <code>dir=\"auto\"<\/code> case is interesting: The browser will look at the first character with strong directionality in the element and use that to set the element\u2019s direction. It\u2019s best to be explicit and use <code>ltr | rtl<\/code>. However, <code>dir=\"auto\"<\/code> can be helpful when presenting content we don\u2019t control, like user-generated text.<\/p>\n<p>Notice that the CSS attribute selectors, <code>[dir=\"ltr\"]<\/code> and <code>[dir=\"rtl\"]<\/code>, don\u2019t target <code>dir=\"auto\"<\/code>. We can use the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/:dir\">:dir() pseudo-class<\/a> for this.<\/p>\n<p><!-- notionvc: 17b9bc85-8251-4f43-a138-0fb483c5cf35 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-pseudo\">:dir(ltr)<\/span> {\n  <span class=\"hljs-attribute\">padding-left<\/span>: <span class=\"hljs-number\">1rem<\/span>;\n  <span class=\"hljs-attribute\">border-left<\/span>: <span class=\"hljs-number\">2px<\/span> solid deepskyblue;\n}\n\n<span class=\"hljs-selector-pseudo\">:dir(rtl)<\/span> {\n  <span class=\"hljs-attribute\">padding-right<\/span>: <span class=\"hljs-number\">1rem<\/span>;\n  <span class=\"hljs-attribute\">border-right<\/span>: <span class=\"hljs-number\">2px<\/span> solid deepskyblue;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_db1765e666b8b2963d3177cc5dbe7cca\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<figure id=\"attachment_75097\" aria-describedby=\"caption-attachment-75097\" style=\"width: 952px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-75097\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/css-dir-pseudo-class.png\" alt=\"The :dir() targets dir=\u201dauto\u201d, since it looks at the browser-resolved direction for the element, not the value of the dir attribute.\" width=\"952\" height=\"568\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/css-dir-pseudo-class.png 952w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/css-dir-pseudo-class-300x179.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/css-dir-pseudo-class-768x458.png 768w\" sizes=\"(max-width: 952px) 100vw, 952px\" \/><figcaption id=\"caption-attachment-75097\" class=\"wp-caption-text\">The :dir() targets dir=\u201dauto\u201d, since it looks at the browser-resolved direction for the element, not the value of the dir attribute.<\/figcaption><\/figure>\n<p>\ud83d\uddd2\ufe0f\u00a0At the time of writing the <code>:dir()<\/code> pseudo-class selector was supported by <a href=\"https:\/\/caniuse.com\/css-dir-pseudo\">approximately 80% of globally used browsers<\/a>. In contrast, attribute selectors like <code>[dir=\"ltr\"]<\/code> enjoy <a href=\"https:\/\/caniuse.com\/mdn-css_selectors_attribute\">nearly 100% coverage<\/a>.<\/p>\n<p>\u270b\u00a0In addition to the HTML <code>dir<\/code> attribute, a CSS <code>direction<\/code> property <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/direction\">also exists<\/a>. However, it\u2019s best practice to prefer the HTML <code>dir<\/code> attribute where possible.<\/p>\n<p>\ud83e\udd3f\u00a0If you\u2019re looking to override text direction inside runs of text, check out the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/bdo\">&lt;bdo&gt; HTML element<\/a>. For isolating elements inside text runs, look at the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/bdi\">&lt;bdi&gt; element<\/a>.<\/p>\n<p>\ud83d\udd17\u00a0Check out the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Global_attributes\/dir\">MDN docs for the dir attribute<\/a> for further reading.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"writing-mode-vertical-layouts\"><\/span>Writing mode (vertical layouts)<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>A lot of East Asian writing \u2014 Chinese, Japanese, Korean, Vietnamese \u2014\u00a0can be written horizontally or vertically. The traditional orientation for these East Asian scripts was top-to-bottom, then right-to-left.<\/p>\n<p>\ud83e\udd3f\u00a0The Wikipedia entry, <a href=\"https:\/\/en.wikipedia.org\/wiki\/Horizontal_and_vertical_writing_in_East_Asian_scripts\">Horizontal and vertical writing in East Asian scripts<\/a>, is an interesting read here.<\/p>\n<p>Today, horizontal, left-to-right orientation for East Asian languages is more common, especially in print and digital. However, modern browsers do support vertical layouts via the <code>writing-mode<\/code> CSS property.<\/p>\n<p><code>writing-mode<\/code> can take one of three values:<\/p>\n<p><!-- notionvc: 23039b42-2401-499c-9b2a-026acf53e444 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.wm-htb<\/span> {\n  <span class=\"hljs-comment\">\/* Horizontal, then top-to-bottom (default). *\/<\/span>\n  <span class=\"hljs-attribute\">writing-mode<\/span>: horizontal-tb;\n}\n\n<span class=\"hljs-selector-class\">.wm-vrl<\/span> {\n  <span class=\"hljs-comment\">\/* Vertical, then right-to-left. *\/<\/span>\n  <span class=\"hljs-attribute\">writing-mode<\/span>: vertical-rl;\n}\n\n<span class=\"hljs-selector-class\">.wm-vlr<\/span> {\n  <span class=\"hljs-comment\">\/* Vertical, then left-to-right. *\/<\/span>\n  <span class=\"hljs-attribute\">writing-mode<\/span>: vertical-lr;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_a9d069d0ec4f2524dda8ffbe6b2900c4\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<figure id=\"attachment_75109\" aria-describedby=\"caption-attachment-75109\" style=\"width: 1040px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-75109\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/writing-mode-en-labeled.png\" alt=\"Three boxes with English text demonstrating the three different values of the writing-mode property.\" width=\"1040\" height=\"598\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/writing-mode-en-labeled.png 1040w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/writing-mode-en-labeled-300x173.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/writing-mode-en-labeled-1024x589.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/writing-mode-en-labeled-768x442.png 768w\" sizes=\"(max-width: 1040px) 100vw, 1040px\" \/><figcaption id=\"caption-attachment-75109\" class=\"wp-caption-text\">Three boxes with English text demonstrating the three different values of the writing-mode property.<\/figcaption><\/figure>\n<p>Of course, vertical modes are meant for East Asian scripts. The following is an example in Japanese. Note that, unlike English, Japanese characters keep a fixed rotation whether they\u2019re displayed horizontally or vertically.<\/p>\n<figure id=\"attachment_75115\" aria-describedby=\"caption-attachment-75115\" style=\"width: 1032px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-75115\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/writing-mode-jp-labelled.png\" alt=\"Three boxes with Japanese text demonstrating the three different values of the writing-mode property.\" width=\"1032\" height=\"616\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/writing-mode-jp-labelled.png 1032w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/writing-mode-jp-labelled-300x179.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/writing-mode-jp-labelled-1024x611.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/writing-mode-jp-labelled-768x458.png 768w\" sizes=\"(max-width: 1032px) 100vw, 1032px\" \/><figcaption id=\"caption-attachment-75115\" class=\"wp-caption-text\">Three boxes with Japanese text demonstrating the three different values of the writing-mode property.<\/figcaption><\/figure>\n<p>\ud83d\uddd2\ufe0f\u00a0With <a href=\"https:\/\/caniuse.com\/css-writing-mode\">nearly 100% global browser support<\/a> you can freely use the <code>writing-mode<\/code> property.<\/p>\n<p>\ud83d\udd17\u00a0Check out the MDN <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/writing-mode\">writing-mode<\/a> entry for more info.<\/p>\n<p>\ud83d\udd17\u00a0<a href=\"https:\/\/www.w3.org\/International\/articles\/vertical-text\/\">Styling vertical Chinese, Japanese, Korean and Mongolian text<\/a> from the W3C is a good concise guide.<\/p>\n<p>\ud83d\udd17\u00a0Play with different writing modes live on the Layout page of our <a href=\"https:\/\/stackblitz.com\/~\/github.com\/PhraseApp-Blog\/css-localization\">StackBlitz demo<\/a>. If you want to run the code on your machine, clone or download our <a href=\"https:\/\/github.com\/PhraseApp-Blog\/css-localization\/tree\/main\">GitHub repo<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"logical-properties\"><\/span>Logical properties<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>CSS layout properties that control element size, position, and spacing have traditionally been physical, like <code>top<\/code> or <code>padding-right<\/code>. The direction of physical properties is effectively based on the screen coordinate system.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-75121\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/physical-properties.png\" alt=\"A diagram of physical CSS properties showing them as based on the screen ie. right is always to the element's right, regardless of text orientation.\" width=\"1040\" height=\"599\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/physical-properties.png 1040w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/physical-properties-300x173.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/physical-properties-1024x590.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/physical-properties-768x442.png 768w\" sizes=\"(max-width: 1040px) 100vw, 1040px\" \/><\/p>\n<p>Physical properties don\u2019t respect <code>dir<\/code> and <code>writing-mode<\/code>: <code>right<\/code> is <em>always<\/em> the physical right side of an element. To accommodate <code>ltr<\/code> and <code>rtl<\/code> layouts, we would have to target these directions and write separate rules for each:<\/p>\n<p><!-- notionvc: f85cd0f5-c4e7-4481-9562-15f4d6bb5e10 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-attr\">&#91;dir=<span class=\"hljs-string\">\"ltr\"<\/span>]<\/span> <span class=\"hljs-selector-class\">.element<\/span> {\n  <span class=\"hljs-attribute\">margin-left<\/span>: <span class=\"hljs-number\">50px<\/span>;\n}\n\n<span class=\"hljs-selector-attr\">&#91;dir=<span class=\"hljs-string\">\"rtl\"<\/span>]<\/span> <span class=\"hljs-selector-class\">.element<\/span> {\n  <span class=\"hljs-attribute\">margin-right<\/span>: <span class=\"hljs-number\">50px<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e0b837211e0765e050f5c0fa4850b249\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-75127\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/physical-margin.png\" alt=\"Diagram showing the element with a left offset when the container direction is ltr and a right offset when the container direction is rtl. The margin is set explicitly for each direction.\" width=\"1041\" height=\"599\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/physical-margin.png 1041w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/physical-margin-300x173.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/physical-margin-1024x589.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/physical-margin-768x442.png 768w\" sizes=\"(max-width: 1041px) 100vw, 1041px\" \/><\/p>\n<p>More recently, however, the CSS standard has adopted <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_logical_properties_and_values\">logical properties<\/a>. Logical properties adapt to both the <code>dir<\/code> and <code>writing-mode<\/code> of an element. Here\u2019s the same example with a logical margin:<\/p>\n<p><!-- notionvc: 341f2b3d-8d7b-4669-9e13-19786806630e --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.element<\/span> {\n  <span class=\"hljs-attribute\">margin-inline-start<\/span>: <span class=\"hljs-number\">50px<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_95f98d36b306f2a2df9f8c81874afdc6\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-75133\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-margin.png\" alt=\"Diagram showing the element with a left offset when the container direction is ltr and a right offset when the container direction is rtl. The margin is set once with margin-inline-start.\" width=\"1041\" height=\"599\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-margin.png 1041w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-margin-300x173.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-margin-1024x589.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-margin-768x442.png 768w\" sizes=\"(max-width: 1041px) 100vw, 1041px\" \/><\/p>\n<p>Notice how we only needed to set the <code>margin-inline-start<\/code> property once: The browser responds to the container\u2019s direction, interpreting <code>inline-start<\/code> as left when <code>dir=\"ltr\"<\/code> and right when <code>dir=\"rtl\"<\/code>.<\/p>\n<p>\ud83d\uddd2\ufe0f\u00a0At the time of writing, logical properties enjoy <a href=\"https:\/\/caniuse.com\/css-logical-props\">95% global browser support<\/a>, so there\u2019s little reason not to prefer them over their physical counterparts.<\/p>\n<p>Logical properties automatically adapt to <code>dir<\/code> and <code>writing-mode<\/code>, and we\u2019ll see this in action as we explore the most important logical properties in the following sections.<\/p>\n<p>\ud83d\udd17\u00a0<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_logical_properties_and_values\">CSS logical properties and values<\/a> on MDN is an excellent resource.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"a-note-on-inline-and-block\"><\/span>A note on inline and block<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>When working with logical properties, we\u2019ll be using <em>inline<\/em> and <em>block<\/em> instead of <em>top<\/em>, <em>down<\/em>, <em>left<\/em>, and <em>right<\/em>.<\/p>\n<ul>\n<li><em>Block dimension<\/em> is the direction that text stacks, like lines in a paragraph. It&#8217;s up and down (vertical) when you&#8217;re reading text written from left to right, like in English. But if you&#8217;re reading text that goes from top to bottom, like in traditional Japanese, the block dimension switches to side-to-side (horizontal).<\/li>\n<li><em>Inline dimension<\/em> is the direction text flows within a line, side-to-side (horizontal) in languages like English, but up and down (vertical) in vertically written text, such as traditional Chinese or Japanese.<\/li>\n<\/ul>\n<p>It\u2019s much easier to understand this with examples, so let\u2019s dive into the properties themselves.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"sizing\"><\/span>Sizing<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Physical CSS sizing is controlled with <code>width<\/code>, <code>height<\/code>, and their <code>min-<\/code> and <code>max-<\/code> equivalents. The logical size counterparts are <code>inline-size<\/code>, <code>block-size<\/code>, with their <code>min-<\/code> and <code>max-<\/code> versions.<\/p>\n<p><!-- notionvc: 5165f98d-28a5-4cd2-9b34-7ff8e5df39c8 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.physical-sizing<\/span> {\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">210px<\/span>;\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-number\">80px<\/span>;\n}\n\n<span class=\"hljs-selector-class\">.logical-sizing<\/span> {\n  <span class=\"hljs-attribute\">inline-size<\/span>: <span class=\"hljs-number\">210px<\/span>;\n  <span class=\"hljs-attribute\">block-size<\/span>: <span class=\"hljs-number\">80px<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_129383a8ceeb6e74be895efb55a75c32\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-75139\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-sizing.png\" alt=\"Image illustrating CSS logical properties with different writing modes. In horizontal writing mode, width maps to inline-size, and height to block-size. In vertical modes, these mappings rotate, demonstrating the adaptability of logical properties to writing mode.\" width=\"1033\" height=\"616\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-sizing.png 1033w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-sizing-300x179.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-sizing-1024x611.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-sizing-768x458.png 768w\" sizes=\"(max-width: 1033px) 100vw, 1033px\" \/><\/p>\n<p>Sizing isn\u2019t affected by <code>dir<\/code>, of course. It <em>is<\/em> affected by <code>writing-mode<\/code>, as seen in the above diagram. Note how the box set with physical <code>width<\/code> and <code>height<\/code> keeps its dimensions regardless of the <code>writing-mode<\/code>. In contrast, <code>inline-size<\/code> and <code>box-size<\/code> cause the box dimensions to adapt to vertical orientations.<\/p>\n<p>It can be helpful to have a mapping between physical properties and their logical equivalents, so here is the simple mapping for sizing properties:<\/p>\n<table style=\"border-collapse: collapse; width: 100%; height: 150px;\">\n<thead>\n<tr style=\"height: 50px;\">\n<td style=\"width: 50%; height: 50px;\">Physical property<\/td>\n<td style=\"width: 50%; height: 50px;\">Logical property (writing-mode: horizontal-tb)<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr style=\"height: 50px;\">\n<td style=\"width: 50%; height: 50px;\">width<\/td>\n<td style=\"width: 50%; height: 50px;\">inline-size<\/td>\n<\/tr>\n<tr style=\"height: 50px;\">\n<td style=\"width: 50%; height: 50px;\">height<\/td>\n<td style=\"width: 50%; height: 50px;\">block-size<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\ud83d\uddd2\ufe0f\u00a0We omit <code>min-<\/code> and <code>max-<\/code> property variants for brevity, but you get the idea.<\/p>\n<p>\ud83d\udd17\u00a0Read more in the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_logical_properties_and_values#properties_for_sizing\">Properties for sizing<\/a> section of the logical properties MDN page.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"positioning\"><\/span>Positioning<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Physical positioning is achieved with <code>inset<\/code>, <code>top<\/code>, <code>right<\/code>, <code>bottom<\/code>, and <code>left<\/code>. For logical positioning, we have <code>inset-block<\/code>, <code>inset-inline<\/code>, with <code>-start<\/code> and <code>-end<\/code> variants.<\/p>\n<p><!-- notionvc: 5583bd55-215b-4121-bdc8-c0b64f745c38 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.physical-positioning<\/span> {\n  <span class=\"hljs-attribute\">position<\/span>: absolute;\n  <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-number\">20px<\/span>;\n  <span class=\"hljs-attribute\">right<\/span>: <span class=\"hljs-number\">20px<\/span>;\n}\n\n<span class=\"hljs-selector-class\">.logical-positioning<\/span> {\n  <span class=\"hljs-attribute\">position<\/span>: absolute;\n  <span class=\"hljs-attribute\">inset-block-start<\/span>: <span class=\"hljs-number\">20px<\/span>;\n  <span class=\"hljs-attribute\">inset-inline-end<\/span>: <span class=\"hljs-number\">20px<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_f7aac3ef3f3c1cd350765f402d5c19e1\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-75145 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-positioning.png\" alt=\"Image showing CSS physical and logical properties for different writing modes and directions. Physical properties 'top' and 'right' change depending on text direction, whereas logical properties 'inset-block-start' and 'inset-inline-end' adapt to the direction and writing mode, ensuring consistent placement across languages and writing modes.\" width=\"1033\" height=\"1142\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-positioning.png 1033w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-positioning-271x300.png 271w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-positioning-926x1024.png 926w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-positioning-768x849.png 768w\" sizes=\"(max-width: 1033px) 100vw, 1033px\" \/><\/p>\n<p>Logical positioning adapts to both <code>dir<\/code> and <code>writing-mode<\/code>. Here are the physical-to-logical positioning mappings:<\/p>\n<table style=\"border-collapse: collapse; width: 100%;\">\n<thead>\n<tr>\n<td style=\"width: 50%;\">Physical property<\/td>\n<td style=\"width: 50%;\">Logical property (writing-mode: horizontal-tb; dir: ltr)<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"width: 50%;\">top<\/td>\n<td style=\"width: 50%;\">inset-block-start<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 50%;\">bottom<\/td>\n<td style=\"width: 50%;\">inset-block-end<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 50%;\">left<\/td>\n<td style=\"width: 50%;\">inset-inline-start<\/td>\n<\/tr>\n<tr>\n<td style=\"width: 50%;\">right<\/td>\n<td style=\"width: 50%;\">inset-inline-end<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\ud83d\udd17\u00a0Read more in the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_logical_properties_and_values#properties_for_positioning\">Properties for positioning<\/a> section of the logical properties MDN page.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"spacing\"><\/span>Spacing<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Our trusty <code>margin<\/code> and <code>padding<\/code> values are how we space elements physically. Their logical equivalents are <code>margin-block<\/code> and <code>margin-inline<\/code>.<\/p>\n<p><!-- notionvc: 82264221-3b1d-45ed-b2c7-b60f482b78e2 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.physical-spacing<\/span> {\n  <span class=\"hljs-attribute\">margin-top<\/span>: <span class=\"hljs-number\">40px<\/span>;\n  <span class=\"hljs-attribute\">padding-right<\/span>: <span class=\"hljs-number\">60px<\/span>;\n}\n\n<span class=\"hljs-selector-class\">.logical-spacing<\/span> {\n  <span class=\"hljs-attribute\">margin-block-start<\/span>: <span class=\"hljs-number\">40px<\/span>;\n  <span class=\"hljs-attribute\">padding-inline-end<\/span>: <span class=\"hljs-number\">60px<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_bf6bbba9dd6f396a434fc08d41d0e2a8\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-75152 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-spacing.png\" alt=\"Image comparing CSS physical and logical spacing properties under various writing modes and directions. It highlights how physical properties like margin-top and padding-right are direction-dependent, while logical properties like margin-block-start and padding-inline-end adapt to the writing orientation.\" width=\"1033\" height=\"686\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-spacing.png 1033w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-spacing-300x199.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-spacing-1024x680.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/logical-spacing-768x510.png 768w\" sizes=\"(max-width: 1033px) 100vw, 1033px\" \/><\/p>\n<p>As you might have guessed, logical margins and paddings adapt to both <code>dir<\/code> and <code>writing-mode<\/code>. Here are the physical-to-logical spacing mappings:<\/p>\n<table style=\"border-collapse: collapse; width: 100%;\">\n<thead>\n<tr>\n<td style=\"width: 50%;\">Physical property<\/td>\n<td style=\"width: 50%;\">Logical property<br \/>\n(writing-mode: horizontal-tb;<br \/>\ndir: ltr)<\/td>\n<\/tr>\n<\/thead>\n<tbody>\n<tr id=\"31f5c975-8e6d-4684-a178-8d3f1472fb30\">\n<td id=\"mI@Y\" class=\"\" style=\"width: 50%;\">margin-top<\/td>\n<td id=\"flcf\" class=\"\" style=\"width: 50%;\">margin-block-start<\/td>\n<\/tr>\n<tr id=\"47056d5f-10a0-49f3-a4bd-ba5000caf29a\">\n<td id=\"mI@Y\" class=\"\" style=\"width: 50%;\">margin-bottom<\/td>\n<td id=\"flcf\" class=\"\" style=\"width: 50%;\">margin-block-end<\/td>\n<\/tr>\n<tr id=\"16b66588-d14f-4c21-b37a-c0329ce1f995\">\n<td id=\"mI@Y\" class=\"\" style=\"width: 50%;\">margin-left<\/td>\n<td id=\"flcf\" class=\"\" style=\"width: 50%;\">margin-inline-start<\/td>\n<\/tr>\n<tr id=\"4f1b0595-a89d-4d9c-9ce9-584c32a11da7\">\n<td id=\"mI@Y\" class=\"\" style=\"width: 50%;\">margin-right<\/td>\n<td id=\"flcf\" class=\"\" style=\"width: 50%;\">margin-inline-end<\/td>\n<\/tr>\n<tr id=\"5ac1b96a-6497-4a91-90d5-bbcf3dc5dbc3\">\n<td id=\"mI@Y\" class=\"\" style=\"width: 50%;\">padding-top<\/td>\n<td id=\"flcf\" class=\"\" style=\"width: 50%;\">padding-block-start<\/td>\n<\/tr>\n<tr id=\"472c7d41-267c-4fa7-a181-8c7fe7f4e570\">\n<td id=\"mI@Y\" class=\"\" style=\"width: 50%;\">padding-bottom<\/td>\n<td id=\"flcf\" class=\"\" style=\"width: 50%;\">padding-block-end<\/td>\n<\/tr>\n<tr id=\"c143a283-efd7-4687-b826-ec7e542689cd\">\n<td id=\"mI@Y\" class=\"\" style=\"width: 50%;\">padding-left<\/td>\n<td id=\"flcf\" class=\"\" style=\"width: 50%;\">padding-inline-start<\/td>\n<\/tr>\n<tr id=\"0a1023a6-87df-4134-b402-81a791974fc8\">\n<td id=\"mI@Y\" class=\"\" style=\"width: 50%;\">padding-right<\/td>\n<td id=\"flcf\" class=\"\" style=\"width: 50%;\">padding-inline-end<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\ud83d\udd17\u00a0Read more in the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_logical_properties_and_values#properties_for_margins\">Properties for margins<\/a> and <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_logical_properties_and_values#properties_for_paddings\">Properties for paddings<\/a> section of the logical properties MDN page.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"other-logical-properties\"><\/span>Other logical properties<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>There are logical properties for <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_logical_properties_and_values#properties_for_borders\">borders<\/a>, <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_logical_properties_and_values#properties_for_border_radius\">border radius<\/a>, and more. To keep things brief, we\u2019ll refer you to the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/CSS_logical_properties_and_values\">full list on MDN<\/a>.<\/p>\n<p>\u270b The logical properties we covered above, along with those for border and border-radius, have excellent modern browser support. For others, always <a href=\"https:\/\/caniuse.com\/\">check browser compatibility<\/a> before using them in production.<\/p>\n<p>\ud83d\udd17\u00a0We cover logical border properties and more in our companion demo. <a href=\"https:\/\/stackblitz.com\/~\/github.com\/PhraseApp-Blog\/css-localization\">Play with it live<\/a> on StackBlitz or <a href=\"https:\/\/github.com\/PhraseApp-Blog\/css-localization\">get the code<\/a> from GitHub.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"fonts\"><\/span>Fonts<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let\u2019s switch gears and look at localization and typography. When it comes to typefaces, want to choose fonts that support all the characters in our language\u2019s scripts e.g. Latin, Arabic, Korean, etc. Some common options are system fonts, multilingual fonts, and Google Noto.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"system-fonts\"><\/span>System fonts<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>A font will often cover a few languages but leave out many. System fonts, however, tend to have wide language coverage and are pre-installed on operating systems. So they\u2019re generally safe to use for multilingual sites. The Windows default system font is Segoe UI, macOS and iOS use San Francisco, and Android uses Roboto.<\/p>\n<p>Here\u2019s a common system font stack:<\/p>\n<p><!-- notionvc: 7275e516-07b2-468c-b34a-7d93ebaeab86 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">html<\/span> {\n  <span class=\"hljs-attribute\">font-family<\/span>: system-ui, <span class=\"hljs-string\">\"Segoe UI\"<\/span>, Roboto, Helvetica,\n    Arial, sans-serif, <span class=\"hljs-string\">\"Apple Color Emoji\"<\/span>, <span class=\"hljs-string\">\"Segoe UI Emoji\"<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_ad508eb2764d650cc9636118bee9ad6f\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<figure id=\"attachment_75158\" aria-describedby=\"caption-attachment-75158\" style=\"width: 1020px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-75158\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/win-system-font.png\" alt=\"The Windows 11 system font displaying characters from different languages.\" width=\"1020\" height=\"1418\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/win-system-font.png 1020w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/win-system-font-216x300.png 216w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/win-system-font-737x1024.png 737w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/win-system-font-768x1068.png 768w\" sizes=\"(max-width: 1020px) 100vw, 1020px\" \/><figcaption id=\"caption-attachment-75158\" class=\"wp-caption-text\">The Windows 11 system font displaying characters from different languages.<\/figcaption><\/figure>\n<figure id=\"attachment_75164\" aria-describedby=\"caption-attachment-75164\" style=\"width: 1078px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-75164\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/macos-system-font.jpg\" alt=\"The macOS system font displaying characters from different languages.\" width=\"1078\" height=\"1340\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/macos-system-font.jpg 1078w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/macos-system-font-241x300.jpg 241w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/macos-system-font-824x1024.jpg 824w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/macos-system-font-768x955.jpg 768w\" sizes=\"(max-width: 1078px) 100vw, 1078px\" \/><figcaption id=\"caption-attachment-75164\" class=\"wp-caption-text\">The macOS system font displaying characters from different languages.<\/figcaption><\/figure>\n<h3><span class=\"ez-toc-section\" id=\"multilingual-fonts\"><\/span>Multilingual fonts<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>While safe, system fonts don\u2019t always represent our brand. We might want to control the fonts on our websites to ensure a consistent experience for our visitors no matter their operating system. Multilingual fonts can fit the bill here, depending on the languages you need covering. Here are some multilingual fonts (keep in mind these are all paid):<\/p>\n<h4><strong>Helvetica World<\/strong><\/h4>\n<p>Languages covered: Arabic, Bulgarian, Bosnian, Catalan, Czech, Danish, German, Greek, English, Spanish, Estonian, Finnish, French, Irish, Hebrew, Croatian, Hungarian, Icelandic, Italian, Lithuanian, Latvian, Maltese, Dutch, Norwegian, Polish, Portuguese, Romanian, Russian, Slovak, Slovenian, Albanian, Serbian, Swedish, Turkish.<\/p>\n<p>\ud83d\udd17\u00a0<a href=\"https:\/\/www.myfonts.com\/collections\/helvetica-world-font-linotype?tab=glyphs&amp;queryId=a0a990be82bf737f84edcf603a416b30&amp;index=universal_search_data&amp;objectIDs=5829869000\">Get Helvetica World from MyFonts<\/a>.<\/p>\n<h4><strong>Neue Helvetica World<\/strong><\/h4>\n<p>Languages covered: Arabic, Bulgarian, Bosnian, Catalan, Czech, Danish, German, Greek, English, Spanish, Estonian, Finnish, French, Irish, Hebrew, Croatian, Hungarian, Icelandic, Italian, Lithuanian, Latvian, Maltese, Dutch, Norwegian, Polish, Portuguese, Romanian, Russian, Slovak, Slovenian, Albanian, Serbian, Swedish, Turkish.<\/p>\n<p>\ud83d\udd17\u00a0<a href=\"https:\/\/www.myfonts.com\/collections\/neue-helvetica-world-font-linotype?queryId=3377711b07f63c2ef2b065c9da93c1c9&amp;eventName=Product%20Clicked&amp;index=universal_search_data&amp;objectIDs=5453355002&amp;positions=2&amp;userquery=helvetica%20world\">Get Neue Helvetica World from MyFonts<\/a>.<\/p>\n<h4><strong>Greta Sans<\/strong><\/h4>\n<p>Languages covered: Arabic, Armenian, Cyrillic, Devanagari, Georgian, Greek, Hebrew, Korean, Latin.<\/p>\n<p>\ud83d\udd17\u00a0<a href=\"https:\/\/www.typotheque.com\/fonts\/greta-sans\">Get Greta Sans from Typothque<\/a>.<\/p>\n<p><!-- notionvc: 6fdb6435-5186-4c42-a718-46c67152f6dc --><\/p>\n<h3><span class=\"ez-toc-section\" id=\"google-noto\"><\/span>Google Noto<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>If a multilingual font meets your needs it could be a good fit for your brand. If, however, you want near-universal language coverage, check out <a href=\"https:\/\/fonts.google.com\/noto\">Google Noto<\/a>. Not only is the Noto font collection free, but it also has outstanding language coverage. Noto isn\u2019t just one font: It\u2019s a system of fonts that were designed to look good and work well together while covering over 1,000 languages!<\/p>\n<p>After assembling our desired Noto collection from <a href=\"https:\/\/fonts.google.com\/?query=noto\">Google Fonts<\/a>, we can target languages with the CSS <code>:lang()<\/code> pseudo-class to set them.<\/p>\n<p><!-- notionvc: d43b6edd-a36b-4c95-8309-94a161f4ac73 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-pseudo\">:lang(en)<\/span> {\n  <span class=\"hljs-attribute\">font-family<\/span>: <span class=\"hljs-string\">\"Noto Serif\"<\/span>, serif;\n}\n\n<span class=\"hljs-selector-pseudo\">:lang(ar)<\/span> {\n  <span class=\"hljs-attribute\">font-family<\/span>: <span class=\"hljs-string\">\"Noto Naskh Arabic\"<\/span>, <span class=\"hljs-string\">\"Noto Serif\"<\/span>, serif;\n}\n\n<span class=\"hljs-selector-pseudo\">:lang(jp)<\/span> {\n  <span class=\"hljs-attribute\">font-family<\/span>: <span class=\"hljs-string\">\"Noto Serif JP\"<\/span>, sans-serif;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_b466271c6af1b864a88f600215ade392\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<figure id=\"attachment_75170\" aria-describedby=\"caption-attachment-75170\" style=\"width: 1080px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-75170\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/noto-font.jpg\" alt=\"Different languages displayed in complimentary Noto fonts.\" width=\"1080\" height=\"1434\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/noto-font.jpg 1080w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/noto-font-226x300.jpg 226w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/noto-font-771x1024.jpg 771w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/noto-font-768x1020.jpg 768w\" sizes=\"(max-width: 1080px) 100vw, 1080px\" \/><figcaption id=\"caption-attachment-75170\" class=\"wp-caption-text\">Different languages displayed in complimentary Noto fonts.<\/figcaption><\/figure>\n<p>\ud83d\udd17\u00a0<a href=\"https:\/\/fonts.google.com\/noto\">Get Noto from Google Fonts<\/a>.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"font-fallback\"><\/span>Font fallback<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>If a font doesn&#8217;t support a particular script, the browser will fall back to a font that does. We can get ahead of this by specifying a fallback font in our CSS. The following uses the Impact font <em>without<\/em> fallback.<\/p>\n<p><!-- notionvc: 4971f935-d8e8-4b4d-90f6-3e533f9a3cf8 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.font-no-fallback<\/span> {\n  <span class=\"hljs-attribute\">font-family<\/span>: Impact;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_e216ee98a8083a3bb6c464a7d9e12546\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Note how non-Latin glyphs fall back to the system font.<\/p>\n<figure id=\"attachment_75176\" aria-describedby=\"caption-attachment-75176\" style=\"width: 1044px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-75176\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/impact-no-fallback.png\" alt=\"A font stack without fallback will cause non-supported glyphs to fall back to the current system font.\" width=\"1044\" height=\"786\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/impact-no-fallback.png 1044w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/impact-no-fallback-300x226.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/impact-no-fallback-1024x771.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/impact-no-fallback-768x578.png 768w\" sizes=\"(max-width: 1044px) 100vw, 1044px\" \/><figcaption id=\"caption-attachment-75176\" class=\"wp-caption-text\">A font stack without fallback will cause non-supported glyphs to fall back to the current system font.<\/figcaption><\/figure>\n<p>Here\u2019s a Noto stack with fallback:<\/p>\n<p><!-- notionvc: 106e31c3-7301-4385-b8aa-1fec3796b91c --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.font-fallback<\/span> {\n  <span class=\"hljs-attribute\">font-family<\/span>: <span class=\"hljs-string\">\"Noto Naskh Arabic\"<\/span>, <span class=\"hljs-string\">\"Noto Serif Devanagari\"<\/span>,\n    <span class=\"hljs-string\">\"Noto Serif SC\"<\/span>, <span class=\"hljs-string\">\"Noto Serif KR\"<\/span>, <span class=\"hljs-string\">\"Noto Serif JP\"<\/span>,\n    <span class=\"hljs-string\">\"Noto Serif\"<\/span>, serif;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_438b54a0a67d3058d5c82e67a30ffe92\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Since we\u2019ve controlled the fallback, we can be confident that our supported language glyphs will be covered.<\/p>\n<figure id=\"attachment_75182\" aria-describedby=\"caption-attachment-75182\" style=\"width: 1032px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-75182\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/noto-fallback-stack.png\" alt=\"A Noto font stack with fallback ensures that we control which font is used for a given glyph.\" width=\"1032\" height=\"822\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/noto-fallback-stack.png 1032w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/noto-fallback-stack-300x239.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/noto-fallback-stack-1024x816.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/noto-fallback-stack-768x612.png 768w\" sizes=\"(max-width: 1032px) 100vw, 1032px\" \/><figcaption id=\"caption-attachment-75182\" class=\"wp-caption-text\">A Noto font stack with fallback ensures that we control which font is used for a given glyph.<\/figcaption><\/figure>\n<p>Of course, this solution means that we need to load all of our fonts on the page, which can be a performance issue. It\u2019s probably wiser to load only the fonts our main page language uses, and use system font fallback for secondary languages.<\/p>\n<p>\ud83d\uddd2\ufe0f\u00a0An alternative is to mix and match, creating our own collection, where different fonts cover different supported subsets of our languages. We cover this briefly in our demo app \u2014 <a href=\"https:\/\/stackblitz.com\/~\/github.com\/PhraseApp-Blog\/css-localization\">play with the demo on StackBlitz<\/a> or <a href=\"https:\/\/github.com\/PhraseApp-Blog\/css-localization\">download the code from GitHub<\/a> and run it on our machine.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"pronunciation-marks\"><\/span>Pronunciation marks<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Some languages add pronunciation text or marks to their scripts, like Ruby or diacritics. Modern CSS allows us to target some of these marks and control their appearance.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"ruby\"><\/span>Ruby<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Ruby annotations are small texts placed above or to the right of the base text to give pronunciation guides, and explanations, or to show the use of characters in East Asian languages.<\/p>\n<p>Ruby has special HTML tags:<\/p>\n<p><!-- notionvc: 6ecefbab-b04b-4b02-a20b-818c469b4ff3 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"jp\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ruby<\/span>&gt;<\/span>\n    \u6771\u4eac <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">rp<\/span>&gt;<\/span>(<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">rp<\/span>&gt;<\/span> <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">rt<\/span>&gt;<\/span>\u3068\u3046\u304d\u3087\u3046<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">rt<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">rp<\/span>&gt;<\/span>)<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">rp<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ruby<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_8e8a4cfd64443ae94e39ba35726368fe\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><code>&lt;ruby&gt;<\/code> contains text that has ruby annotations. The annotations themselves go inside <code>&lt;rt&gt;<\/code> tags. <code>&lt;rp&gt;<\/code> tags are used for ruby parentheses shown to browsers that don\u2019t support Ruby.<\/p>\n<p>\ud83d\udd17\u00a0Read about the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/ruby\">&lt;ruby&gt;<\/a>, <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/rt\">&lt;rt&gt;<\/a>, and <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/rp\">&lt;rp&gt;<\/a> tags on MDN.<\/p>\n<p>\u270b\u00a0While over 98% of globally used browsers support basic ruby, complex ruby and <code>writing-mode<\/code> with ruby are only partially supported by modern browsers. Check out <a href=\"https:\/\/caniuse.com\/ruby\">Can I use<\/a> for the latest info on ruby support.<\/p>\n<p>We can control ruby using the <code>ruby-position<\/code> CSS property.<\/p>\n<p><!-- notionvc: 56e0c67a-7550-463a-8ce7-5428f8c69a23 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.ruby-over<\/span> {\n  <span class=\"hljs-attribute\">ruby-position<\/span>: over;\n}\n\n<span class=\"hljs-selector-class\">.ruby-under<\/span> {\n  <span class=\"hljs-attribute\">ruby-position<\/span>: under;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_8e5946a7ccd963578c3ecd70d74749dd\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<figure id=\"attachment_75188\" aria-describedby=\"caption-attachment-75188\" style=\"width: 820px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-75188\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/ruby-position.png\" alt=\"Japanese text shown with ruby annotations above and below.\" width=\"820\" height=\"276\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/ruby-position.png 820w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/ruby-position-300x101.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/ruby-position-768x258.png 768w\" sizes=\"(max-width: 820px) 100vw, 820px\" \/><figcaption id=\"caption-attachment-75188\" class=\"wp-caption-text\">Japanese text shown with ruby annotations above and below.<\/figcaption><\/figure>\n<p>\ud83d\udd17\u00a0More info about the <code>ruby-position<\/code> property can be found <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/ruby-position\">on MDN<\/a>.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"text-emphasis\"><\/span>Text emphasis<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>East Asian languages often use text emphasis marks to highlight text instead of bold or italics. We can control text emphasis style, color, and position with CSS.<\/p>\n<p><!-- notionvc: 07f7fe92-6ba2-4d3e-af0b-60e3299370d3 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"jp\"<\/span>&gt;<\/span>\n  \u660e\u65e5\u306f<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"text-emphasis-dot\"<\/span>&gt;<\/span>\u6674\u308c<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\u3067\u3059\u3002\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">lang<\/span>=<span class=\"hljs-string\">\"jp\"<\/span>&gt;<\/span>\n  \u660e\u65e5\u306f<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>\n    <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"text-emphasis-sesame text-emphasis-under-left\"<\/span>\n  &gt;<\/span>\u6674\u308c<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>\n  &gt;<\/span>\u3067\u3059\u3002\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">em<\/span><span class=\"hljs-selector-pseudo\">:lang(jp)<\/span> {\n  <span class=\"hljs-attribute\">font-style<\/span>: normal;\n  <span class=\"hljs-attribute\">text-emphasis-color<\/span>: deepskyblue;\n  <span class=\"hljs-attribute\">text-emphasis-position<\/span>: over right;\n}\n\n<span class=\"hljs-selector-class\">.text-emphasis-dot<\/span><span class=\"hljs-selector-pseudo\">:lang(jp)<\/span> {\n  <span class=\"hljs-attribute\">text-emphasis-style<\/span>: dot;\n}\n\n<span class=\"hljs-selector-class\">.text-emphasis-sesame<\/span><span class=\"hljs-selector-pseudo\">:lang(jp)<\/span> {\n  <span class=\"hljs-attribute\">text-emphasis-style<\/span>: sesame;\n}\n\n<span class=\"hljs-selector-class\">.text-emphasis-under-left<\/span><span class=\"hljs-selector-pseudo\">:lang(jp)<\/span> {\n  <span class=\"hljs-attribute\">text-emphasis-position<\/span>: under left;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_c995f0971570be845cbaf1711c869be7\" class=\"pxblock pxblock--text alignfull spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-75194\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/text-emphasis.png\" alt=\"Two examples of Japanese writing with text emphasis, one with marks above the characters and one below.\" width=\"824\" height=\"266\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/text-emphasis.png 824w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/text-emphasis-300x97.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2024\/02\/text-emphasis-768x248.png 768w\" sizes=\"(max-width: 824px) 100vw, 824px\" \/><\/p>\n<p>\ud83d\uddd2\ufe0f\u00a0Japanese, Korean, Mongolian, and Chinese each have a preferred position for text emphasis and ruby, depending on whether they\u2019re horizontal or vertical. <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/text-emphasis-position#description\">MDN has a handy table<\/a> that shows these preferences.<\/p>\n<p>\ud83d\udd17\u00a0<a href=\"https:\/\/stackblitz.com\/~\/github.com\/PhraseApp-Blog\/css-localization\">Play with live demo app<\/a> on StackBlitz; <a href=\"https:\/\/github.com\/PhraseApp-Blog\/css-localization\">get all the demo app code<\/a> from GitHub. We cover setting quotes per language in the demo among other things.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"the-joy-of-css\"><\/span>The joy of CSS<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>That about does it for this guide. We hope our exploration into CSS localization has been as enlightening and enjoyable, and that it showed you a few things about the untapped potential of CSS localization. Here&#8217;s to creating vibrant, global web experiences with the ever-growing awesomeness of CSS. Happy coding!<\/p>\n<p><!-- notionvc: ae508e2b-f8b8-4a5b-97fe-c3b0e5541dd5 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>CSS is surprisingly important when it comes to site localization. Let&#8217;s take a look at some ways of providing a delightful localized UX.<\/p>\n","protected":false},"author":41,"featured_media":2612,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_stopmodifiedupdate":false,"_modified_date":"","_searchwp_excluded":"","footnotes":""},"categories":[40],"class_list":["post-10151","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software-localization"],"acf":[],"_links":{"self":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/10151"}],"collection":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/comments?post=10151"}],"version-history":[{"count":6,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/10151\/revisions"}],"predecessor-version":[{"id":75203,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/10151\/revisions\/75203"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/media\/2612"}],"wp:attachment":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/media?parent=10151"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/categories?post=10151"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}