{"id":9750,"date":"2026-05-29T07:57:47","date_gmt":"2026-05-29T12:57:47","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=9750"},"modified":"2026-05-29T07:57:48","modified_gmt":"2026-05-29T12:57:48","slug":"the-fundamentals-and-dev-experience-of-css-function","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/the-fundamentals-and-dev-experience-of-css-function\/","title":{"rendered":"The Fundamentals and Dev Experience of CSS @function"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">CSS has introduced <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/Reference\/At-rules\/@function\">functions<\/a> so authors can encapsulate and reuse property behaviors across their style sheets without duplicating the code or polluting the DOM with single-use intermediate <code>--_variables<\/code><sup data-fn=\"aa0d6c75-4052-4e80-ae97-53a64e1b0182\" class=\"fn\"><a href=\"#aa0d6c75-4052-4e80-ae97-53a64e1b0182\" id=\"aa0d6c75-4052-4e80-ae97-53a64e1b0182-link\">1<\/a><\/sup>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There are a lot of really cool and useful things we can do with functions. In this fundamentals article, we will go over several CSS gotchas that form the bumpers on our bowling lane for the strike we&#8217;ll hit in the follow-up, and get a good sense of what they are and what they aren&#8217;t.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"686\" height=\"386\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/ZlIhRzHQ.png?resize=686%2C386&#038;ssl=1\" alt=\"Image of a bowling lane with bumpers and a bowling ball edited on top of it with arrows bouncing in a zig-zag off of the rails 3 times before hitting the center of the pins likely for a strike\" class=\"wp-image-9753\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/ZlIhRzHQ.png?w=686&amp;ssl=1 686w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/ZlIhRzHQ.png?resize=300%2C169&amp;ssl=1 300w\" sizes=\"auto, (max-width: 686px) 100vw, 686px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This is a custom CSS function:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@function<\/span> --hello-world() {\n  <span class=\"hljs-selector-tag\">result<\/span>: \"<span class=\"hljs-selector-tag\">Hello<\/span> <span class=\"hljs-selector-tag\">World<\/span>\";\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><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<p class=\"wp-block-paragraph\">And this is how you call it:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">body<\/span><span class=\"hljs-selector-pseudo\">::after<\/span> {\n  <span class=\"hljs-attribute\">content<\/span>: <span class=\"hljs-built_in\">--hello-world<\/span>() <span class=\"hljs-string\">\"!\"<\/span>;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><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 class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_yyVgvvR\/3cdffc9dc28eada507e521f31efda2af\" src=\"\/\/codepen.io\/anon\/embed\/yyVgvvR\/3cdffc9dc28eada507e521f31efda2af?height=450&amp;theme-id=1&amp;slug-hash=yyVgvvR\/3cdffc9dc28eada507e521f31efda2af&amp;default-tab=css,result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed yyVgvvR\/3cdffc9dc28eada507e521f31efda2af\" title=\"CodePen Embed yyVgvvR\/3cdffc9dc28eada507e521f31efda2af\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Soon, but not yet, we will be able to set multiple different properties with distinct values from a single function call by returning a comma-separated result and splitting it into multiple properties.<\/p>\n\n\n\n<p class=\"learn-more wp-block-paragraph\"><strong>Gotcha:<\/strong><br>For now, functions can only return a <strong>single property value<\/strong> (or just <em>part<\/em> of one).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you set the value of a <code>--variable<\/code> by calling a custom function, you can reference that <code>--variable<\/code> any number of times and copy the same singular result wherever you need it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Function Encapsulation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You can set intermediate variables inside the function to help with the final result:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@function<\/span> --the-answer() {\n  <span class=\"hljs-selector-tag\">--a<\/span>: 4<span class=\"hljs-selector-tag\">px<\/span>;\n  <span class=\"hljs-selector-tag\">--b<\/span>: 10;\n  <span class=\"hljs-selector-tag\">--c<\/span>: 2<span class=\"hljs-selector-tag\">px<\/span>;\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--a<\/span>) * <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--b<\/span>) + <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--c<\/span>));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><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<p class=\"wp-block-paragraph\">Those intermediate variables do not leak onto the element; they are internal, private variables:<\/p>\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-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-built_in\">--the-answer<\/span>();\n  <span class=\"hljs-comment\">\/* --a, --b, and --c are NOT defined here! *\/<\/span>\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<p class=\"learn-more wp-block-paragraph\"><strong>Gotcha:<\/strong><br>Custom properties within functions are so private that not even the global registration can type them.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@property<\/span> --a {\n  <span class=\"hljs-selector-tag\">syntax<\/span>: \"&lt;<span class=\"hljs-selector-tag\">color<\/span>&gt;\";\n  <span class=\"hljs-selector-tag\">initial-value<\/span>: <span class=\"hljs-selector-tag\">hotpink<\/span>;\n  <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>;\n}\n\n<span class=\"hljs-keyword\">@function<\/span> --the-answer() {\n  <span class=\"hljs-selector-tag\">--a<\/span>: 4<span class=\"hljs-selector-tag\">px<\/span>; <span class=\"hljs-comment\">\/* uses the value 4px and doesn't break *\/<\/span>\n  <span class=\"hljs-selector-tag\">--b<\/span>: 10;\n  <span class=\"hljs-selector-tag\">--c<\/span>: 2<span class=\"hljs-selector-tag\">px<\/span>;\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--a<\/span>) * <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--b<\/span>) + <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--c<\/span>));\n}\n\n<span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-built_in\">--the-answer<\/span>();\n  <span class=\"hljs-attribute\">--a<\/span>: <span class=\"hljs-number\">4px<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">var<\/span>(--a);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><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<p class=\"wp-block-paragraph\">The body&#8217;s <code>padding<\/code> is <code>42px<\/code> and the background is <code>hotpink<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Function Arguments<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You can call functions with arguments:<\/p>\n\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\">body<\/span> {\n  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-built_in\">--the-answer<\/span>(<span class=\"hljs-number\">99<\/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<p class=\"learn-more wp-block-paragraph\"><strong>Gotcha:<\/strong><br>The function above fails silently instead of a more friendly DX that leaves you with <em>something<\/em> to debug. <strong>You cannot call a function with more parameters than the <code>@function<\/code> defined.<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">They might, hopefully, improve the DX here and instead just ignore extra parameters in the future! \ud83e\udd1e\ud83d\udc7d<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Fortunately, defining any number of arguments is easy:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@function<\/span> --the-answer(--arg1, --arg2, --arg3) {\n  <span class=\"hljs-selector-tag\">--a<\/span>: 4<span class=\"hljs-selector-tag\">px<\/span>;\n  <span class=\"hljs-selector-tag\">--b<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--arg1<\/span>) <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--arg1<\/span>) + 10);\n  <span class=\"hljs-selector-tag\">--c<\/span>: 2<span class=\"hljs-selector-tag\">px<\/span>;\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--a<\/span>) * <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--b<\/span>) + <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--c<\/span>));\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><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<p class=\"learn-more wp-block-paragraph\"><strong>Gotcha<\/strong>:<br>If you call a function with <em>too few <\/em>arguments, it also fails silently instead of leaving you with <em>something<\/em> to debug.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But, you can give the arguments default values, <em>and then they become optional<\/em>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@function<\/span> --the-answer(--required, --arg2: <span class=\"hljs-number\">0px<\/span>, --arg3: initial) {\n  <span class=\"hljs-selector-tag\">--a<\/span>: 4<span class=\"hljs-selector-tag\">px<\/span>;\n  <span class=\"hljs-selector-tag\">--b<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--required<\/span>) <span class=\"hljs-selector-tag\">-<\/span> <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--required<\/span>) + 10);\n  <span class=\"hljs-selector-tag\">--c<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">clamp<\/span>(1<span class=\"hljs-selector-tag\">px<\/span>, <span class=\"hljs-selector-tag\">round<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--arg2<\/span>)), 1<span class=\"hljs-selector-tag\">px<\/span>) * 2);\n\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--a<\/span>) * <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--b<\/span>) + <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--c<\/span>));\n}\n\n<span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">padding<\/span>: <span class=\"hljs-built_in\">--the-answer<\/span>(<span class=\"hljs-number\">99<\/span>);\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><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<p class=\"wp-block-paragraph\">The body&#8217;s <code>padding<\/code> is a resilient <code>42px<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>initial<\/code> value is particularly useful as a default because it allows you to use <code>var(--arg3, fallbacks)<\/code> in the implementation and branch the behavior.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>It would be great if<\/em> <code>initial<\/code> <em>became the default argument instead of the silent-failure DX.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You could also branch by using <code>if(style())<\/code> on the arguments <a href=\"https:\/\/css-tip.com\/if-trick\/\">with several more gotchas<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Argument Typing and Fake Arguments for Typed Encapsulation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">You can specify argument types, and as a hack before official alternatives, intentionally add superfluous, undocumented, unused, typed parameters (with defaults) to have pseudo-registered var behavior inside the function (since the global registration doesn&#8217;t reach inside):<\/p>\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-keyword\">@function<\/span> --divide-by-<span class=\"hljs-number\">3<\/span>(--a &lt;number&gt;, --_pi-ish &lt;integer&gt;: -<span class=\"hljs-number\">1<\/span>) {\n  <span class=\"hljs-selector-tag\">--_pi-ish<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(3<span class=\"hljs-selector-class\">.14<\/span>);\n  <span class=\"hljs-comment\">\/* ^ becomes 3 because it's an integer type *\/<\/span>\n\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--a<\/span>) \/ <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--_pi-ish<\/span>));\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<p class=\"wp-block-paragraph\">Typed vars inside a function have a critically fantastic benefit over the usual registered var &#8211; they become <code>initial<\/code> if the value doesn&#8217;t compute into the specified type, <strong>which means you can use computed fallbacks<\/strong>!<\/p>\n\n\n\n<p class=\"learn-more wp-block-paragraph\"><strong>Gotcha:<\/strong><br>In the global behavior, a registered variable referenced with the <code>var()<\/code> function causes the <code>var()<\/code> fallback to become <em>unreachable<\/em>. \ud83d\ude35\u200d\ud83d\udcab\ud83e\udea6<\/p>\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-keyword\">@property<\/span> --a {\n  <span class=\"hljs-selector-tag\">syntax<\/span>: \"&lt;<span class=\"hljs-selector-tag\">number<\/span>&gt;\";\n  <span class=\"hljs-selector-tag\">initial-value<\/span>: 0;\n  <span class=\"hljs-selector-tag\">inherits<\/span>: <span class=\"hljs-selector-tag\">true<\/span>;\n}\n\n<span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">--a<\/span>: pizza;\n  <span class=\"hljs-attribute\">--divide-by<\/span>: <span class=\"hljs-number\">3<\/span>;\n\n  <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-built_in\">var<\/span>(--a,\n    calc(\n      <span class=\"hljs-number\">1<\/span> \/ var(--divide-by)\n    )\n  );\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<p class=\"wp-block-paragraph\">Opacity resolved to 0 because the <code>calc()<\/code> in the fallback is unreachable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Compare this to a custom function implementation:<\/p>\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-keyword\">@function<\/span> --opacity(--a &lt;number&gt;, --divide-by &lt;integer&gt;: -<span class=\"hljs-number\">1<\/span>) {\n  <span class=\"hljs-selector-tag\">--divide-by<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(3<span class=\"hljs-selector-class\">.14<\/span>);\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--a<\/span>, <span class=\"hljs-selector-tag\">calc<\/span>(1 \/ <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--divide-by<\/span>)));\n}\n<span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-built_in\">--opacity<\/span>(pizza);\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<p class=\"wp-block-paragraph\">Opacity resolves to <code>0.3333<\/code> because <code>pizza<\/code> isn&#8217;t a number so <code>--a<\/code> became <code>initial<\/code> and the fallback <code>calc()<\/code> was executed instead.<\/p>\n\n\n\n<p class=\"learn-more wp-block-paragraph\"><strong>Gotcha:<\/strong> <br>Without that <code>calc()<\/code> wrapping <code>3.14<\/code>, an <code>integer<\/code>-typed argument will fail to <code>initial<\/code> <em>because the decimal syntax is rejected as non-integer<\/em> before computed value time.<\/p>\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-keyword\">@function<\/span> --opacity(--a &lt;number&gt;, --divide-by &lt;integer&gt;: -<span class=\"hljs-number\">1<\/span>) {\n  <span class=\"hljs-selector-tag\">--divide-by<\/span>: 3<span class=\"hljs-selector-class\">.14<\/span>;\n\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--a<\/span>, <span class=\"hljs-selector-tag\">calc<\/span>(1 \/ <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--divide-by<\/span>, 2)));\n}\n<span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-built_in\">--opacity<\/span>(pizza);\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<p class=\"wp-block-paragraph\">Opacity resolves to <code>0.5<\/code> because <code>pizza<\/code> isn&#8217;t a number so <code>--a<\/code> became <code>initial<\/code>, the fallback <code>calc()<\/code> was executed, and <code>--divide-by<\/code> also used its fallback of <code>2<\/code> because the <code>3.14<\/code> assignment failed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Comma-Separated Arguments<\/h2>\n\n\n\n<p class=\"learn-more wp-block-paragraph\"><strong>Gotcha:<\/strong> <br>The only place in all of CSS where a variable doesn&#8217;t effectively expand in place is in the parameters when calling a <strong>custom<\/strong> function.<\/p>\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-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">--rgb<\/span>: <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">255<\/span>, <span class=\"hljs-number\">0<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">rgb<\/span>(var(--rgb));\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<p class=\"wp-block-paragraph\">The background is bright green.<\/p>\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-keyword\">@function<\/span> --rgbFn(--r, --g, --b) {\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">rgb<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--r<\/span>), <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--g<\/span>), <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--b<\/span>));\n}\n\n<span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">--rgb<\/span>: <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">255<\/span>, <span class=\"hljs-number\">0<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">--rgbFn<\/span>(var(--rgb));\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<p class=\"wp-block-paragraph\">The function call failed because all 3 parameters were stuffed into the &#8211;r argument. <a href=\"https:\/\/github.com\/w3c\/csswg-drafts\/issues\/13927#issue-4449327153\">I am very hopeful this will be fixed<\/a>.<\/p>\n\n\n\n<p class=\"learn-more wp-block-paragraph\"><strong>Gotcha:<\/strong><br>There is an implemented syntax to deliberately cause anti-spreading by wrapping them in curly braces.<\/p>\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-keyword\">@function<\/span> --rgbFn(--rgbArg) {\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">rgb<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--rgbArg<\/span>));\n}\n\n<span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">--r<\/span>: <span class=\"hljs-number\">0<\/span>;\n  <span class=\"hljs-attribute\">--g<\/span>: <span class=\"hljs-number\">255<\/span>;\n  <span class=\"hljs-attribute\">--b<\/span>: <span class=\"hljs-number\">0<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">--rgbFn<\/span>({ var(--r), <span class=\"hljs-built_in\">var<\/span>(--g), <span class=\"hljs-built_in\">var<\/span>(--b) });\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<p class=\"wp-block-paragraph\">The background is bright green. For consistency, it was originally planned <em>and briefly even implemented by Anders of the Chrome team<\/em> (who has implemented almost every awesome feature I&#8217;ve played with over the years!) that comma-separated <code>var()<\/code> values auto-spread just like normal, so you would wrap <code>var()<\/code> with the braces intentionally for the same anti-spread effect.<\/p>\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-keyword\">@function<\/span> --rgbFn(--rgbArg) {\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">rgb<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--rgbArg<\/span>));\n}\n<span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">--rgb<\/span>: <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-number\">255<\/span>, <span class=\"hljs-number\">0<\/span>;\n  <span class=\"hljs-attribute\">background<\/span>: <span class=\"hljs-built_in\">--rgbFn<\/span>({ var(--rgb) });\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<p class=\"wp-block-paragraph\">The background is bright green.<\/p>\n\n\n\n<p class=\"learn-more wp-block-paragraph\">This anti-spread around variables <em>is<\/em> still implemented, so it would be a great idea to wrap your comma-separated <code>var()<\/code> arguments (csvarguments) in curly braces ahead of the restoration\/fix if they move forward with it. Though apart from <a href=\"https:\/\/propjockey.github.io\/doubledash.css\/#repeat\">a custom repeat function<\/a> and a <a href=\"https:\/\/propjockey.github.io\/doubledash.css\/#loop\">custom loop function<\/a>, there are currently no use \u2014 because there is no processing possible yet \u2014 and so it must be used as-is. That is, there is no functionality a standard <code>--var<\/code> can&#8217;t already do to a csvargument, making it pointless to pass to a custom function. So you probably haven&#8217;t done that yet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Once csvarguments spread for calling custom functions like they do for calling standard functions (and like they do for <em>everything else in CSS<\/em>), we will have <strong><em>hundreds<\/em><\/strong> of new possibilities available to us, including returning multiple values from a single function call since we could trivially make an <code>--nth-item()<\/code> function to pick each piece returned from a list.<\/p>\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-keyword\">@function<\/span> --nth-item(--nth, --p0, --p1) {\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">if<\/span>(\n    <span class=\"hljs-selector-tag\">style<\/span>(<span class=\"hljs-selector-tag\">--nth<\/span>: 0): <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--p0<\/span>);\n    <span class=\"hljs-selector-tag\">style<\/span>(<span class=\"hljs-selector-tag\">--nth<\/span>: 1): <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--p1<\/span>);\n    <span class=\"hljs-selector-tag\">else<\/span>: <span class=\"hljs-selector-tag\">black<\/span>;\n  );\n}\n\n<span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">--x<\/span>: <span class=\"hljs-number\">1<\/span>;\n  <span class=\"hljs-attribute\">--arrayOfArgs<\/span>: skyblue, lime;\n  <span class=\"hljs-attribute\">--bg<\/span>: <span class=\"hljs-built_in\">--nth-item<\/span>(var(--x), <span class=\"hljs-built_in\">var<\/span>(--arrayOfArgs));\n  <span class=\"hljs-attribute\">background-color<\/span>: <span class=\"hljs-built_in\">var<\/span>(--bg);\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<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s the majority of our lane! We&#8217;re a bit in the weeds of the CaveatSandStorm but if you have followed this and can navigate these behaviors, you&#8217;re <em>far, far<\/em> along the path to mastering CSS variables <em>and scraping the potential of custom CSS functions<\/em>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Function Results<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Here are a few more notes for the foundation of custom CSS functions. We can also specify the type of the <code>result<\/code> with a <code>returns<\/code> directive after the arguments:<\/p>\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-keyword\">@function<\/span> --opacity(--a &lt;number&gt;, --divide-by &lt;integer&gt;: -<span class=\"hljs-number\">1<\/span>) returns &lt;number&gt; {\n  <span class=\"hljs-selector-tag\">--divide-by<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(3<span class=\"hljs-selector-class\">.14<\/span>);\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--a<\/span>, <span class=\"hljs-selector-tag\">calc<\/span>(1 \/ <span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--divide-by<\/span>, 2)));\n}\n<span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-built_in\">--opacity<\/span>(pizza); <span class=\"hljs-comment\">\/* 0.3333 *\/<\/span>\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<p class=\"learn-more wp-block-paragraph\"><strong>Gotcha:<\/strong> <br>Like the arguments, if your <code>result<\/code> doesn&#8217;t match the <code>return<\/code> type, your function will return <code>initial<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Functions can call other functions.<\/p>\n\n\n\n<p class=\"learn-more wp-block-paragraph\"><strong>Gotcha:<\/strong> <br>Functions can&#8217;t currently call themselves. No recursion is allowed because CSS treats it as cyclic and fails to <code>initial<\/code>.<\/p>\n\n\n\n<p class=\"learn-more wp-block-paragraph\"><strong>Gotcha:<\/strong><br>Functions can return a value <strong>and you<\/strong> <strong><em>can&#8217;t<\/em><\/strong> <strong>pass that value back into the same function<\/strong> elsewhere. This <a href=\"https:\/\/issues.chromium.org\/issues\/514021797\">feels like a bug<\/a> and is alarming. Until that\u2019s fixed, most math-based custom functions that aren\u2019t trivial calc()s are DOA along with anything empowering dynamic composition. Pre-publish edit: Tab has chimed in on the Chrome technically-not-a-bug that I filed and identified it as a spec-level-bug and <a href=\"https:\/\/github.com\/w3c\/csswg-drafts\/issues\/13943\">they will fix it soon<\/a>!<\/p>\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-keyword\">@function<\/span> --add-a-quarter(--a &lt;number&gt;) returns &lt;number&gt; {\n  <span class=\"hljs-selector-tag\">result<\/span>: <span class=\"hljs-selector-tag\">calc<\/span>(<span class=\"hljs-selector-tag\">var<\/span>(<span class=\"hljs-selector-tag\">--a<\/span>) + 0<span class=\"hljs-selector-class\">.25<\/span>);\n}\n<span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">--quarter<\/span>: <span class=\"hljs-built_in\">--add-a-quarter<\/span>(<span class=\"hljs-number\">0<\/span>);\n  <span class=\"hljs-attribute\">--half<\/span>: <span class=\"hljs-built_in\">--add-a-quarter<\/span>(var(--quarter));\n  <span class=\"hljs-attribute\">opacity<\/span>: <span class=\"hljs-built_in\">var<\/span>(--half);\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<p class=\"wp-block-paragraph\"><code>--half<\/code> is <code>initial<\/code> \ud83d\ude35\u200d\ud83d\udcab\ud83e\udea6\ud83d\udc94 (this will work correctly at a later date!)<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Gotcha Cascade<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To review the developer experience of CSS Custom Functions, here are all the gotchas we ran into just covering the basics.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Soon, but not yet, we will be able to set multiple different values from a single function call.<\/li>\n\n\n\n<li>Variables internal to a function are so private that not even the global registration can type them.<\/li>\n\n\n\n<li>Calling a function with too many parameters fails silently instead of returning <em>something<\/em> for you to debug.<\/li>\n\n\n\n<li>If you call a function with too few arguments and those arguments don&#8217;t have default values, it also fails silently instead of leaving you with <em>something<\/em> to debug.<\/li>\n\n\n\n<li>In the global behavior, a registered variable referenced with the <code>var()<\/code> function causes the <code>var()<\/code> fallback to become <em>unreachable<\/em>.<\/li>\n\n\n\n<li>Without <code>calc()<\/code> wrapping <code>3.14<\/code> on hardcoded assignment to an <code>integer<\/code> typed argument, it will fail to <code>initial<\/code> <em>because the decimal syntax is rejected as non-integer<\/em> before computed value time.<\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/w3c\/csswg-drafts\/issues\/13927#issue-4449327153\">For the moment<\/a>, the only place in all of CSS where a variable doesn&#8217;t effectively expand in place is in the parameters when calling a <strong>custom<\/strong> function.<\/li>\n\n\n\n<li>There is an implemented syntax to deliberately cause anti-spreading of csvarguments by wrapping them in curly braces.<\/li>\n\n\n\n<li>Like the arguments, if your function <code>result<\/code> doesn&#8217;t match its <code>return<\/code> type, your function will return <code>initial<\/code>.<\/li>\n\n\n\n<li>Functions can&#8217;t currently call themselves. No recursion is allowed. CSS treats it as cyclic and fails to <code>initial<\/code>.<\/li>\n\n\n\n<li>Functions can return a value <strong>and you<\/strong> <strong><em>can&#8217;t<\/em><\/strong> <strong>pass that value back into the same function<\/strong> elsewhere. This is a spec bug, and is <a href=\"https:\/\/github.com\/w3c\/csswg-drafts\/issues\/13943\">being fixed by Tab<\/a>! \ud83d\ude4f<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Just the Beginning<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Overall, the DX for CSS custom functions, as they are now, is &#8230; not good. But there&#8217;s a <a href=\"https:\/\/propjockey.github.io\/doubledash.css\">ton of potential<\/a> and a lot you <em>can<\/em> do now, even if it&#8217;s mostly shallow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s the foundation, next time I will dive into what I&#8217;m most excited to share with you; <strong>The Scope of CSS @\u200dfunction<\/strong>. Until then, <a href=\"https:\/\/bsky.app\/profile\/janeori.propjockey.io\">I invite Open Contact<\/a> \ud83d\udc9a\ud83d\udc7d. <\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n<ol class=\"wp-block-footnotes\"><li id=\"aa0d6c75-4052-4e80-ae97-53a64e1b0182\">CSS Library and Component Authors have long used the convention of underscores before or after a prefix on CSS variables to distinguish private\/internal behavior vs dev-user exposed API variables. Now, we have an official lane for private variables! \ud83c\udf89 <a href=\"#aa0d6c75-4052-4e80-ae97-53a64e1b0182-link\" aria-label=\"Jump to footnote reference 1\">\u21a9\ufe0e<\/a><\/li><\/ol>\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>There are quite a few &#8220;gotchas,&#8221; developers face when getting into the new @function syntax of CSS. Some are getting addressed! <\/p>\n","protected":false},"author":50,"featured_media":9813,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"[{\"content\":\"CSS Library and Component Authors have long used the convention of underscores before or after a prefix on CSS variables to distinguish private\/internal behavior vs dev-user exposed API variables. Now, we have an official lane for private variables! \ud83c\udf89\",\"id\":\"aa0d6c75-4052-4e80-ae97-53a64e1b0182\"}]"},"categories":[1],"tags":[395,7],"class_list":["post-9750","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-function","tag-css"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/05\/function.jpg?fit=2000%2C1200&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9750","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/50"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=9750"}],"version-history":[{"count":20,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9750\/revisions"}],"predecessor-version":[{"id":9851,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/9750\/revisions\/9851"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/9813"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=9750"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=9750"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=9750"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}