922 lines
1.7 MiB
HTML
Raw Normal View History

2025-01-12 00:52:51 +08:00
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta http-equiv="X-UA-Compatible" content="IE=EDGE" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="author" content="Carson Sievert" />
<meta name="date" content="2024-03-15" />
<title>Overview of the sass R package</title>
<script>// Pandoc 2.9 adds attributes on both header and div. We remove the former (to
// be compatible with the behavior of Pandoc < 2.8).
document.addEventListener('DOMContentLoaded', function(e) {
var hs = document.querySelectorAll("div.section[class*='level'] > :first-child");
var i, h, a;
for (i = 0; i < hs.length; i++) {
h = hs[i];
if (!/^h[1-6]$/i.test(h.tagName)) continue; // it should be a header h1-h6
a = h.attributes;
while (a.length > 0) h.removeAttribute(a[0].name);
}
});
</script>
<style type="text/css">
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
</style>
<style type="text/css">
code {
white-space: pre;
}
.sourceCode {
overflow: visible;
}
</style>
<style type="text/css" data-origin="pandoc">
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; }
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; }
code span.at { color: #7d9029; }
code span.bn { color: #40a070; }
code span.bu { color: #008000; }
code span.cf { color: #007020; font-weight: bold; }
code span.ch { color: #4070a0; }
code span.cn { color: #880000; }
code span.co { color: #60a0b0; font-style: italic; }
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; }
code span.do { color: #ba2121; font-style: italic; }
code span.dt { color: #902000; }
code span.dv { color: #40a070; }
code span.er { color: #ff0000; font-weight: bold; }
code span.ex { }
code span.fl { color: #40a070; }
code span.fu { color: #06287e; }
code span.im { color: #008000; font-weight: bold; }
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; }
code span.kw { color: #007020; font-weight: bold; }
code span.op { color: #666666; }
code span.ot { color: #007020; }
code span.pp { color: #bc7a00; }
code span.sc { color: #4070a0; }
code span.ss { color: #bb6688; }
code span.st { color: #4070a0; }
code span.va { color: #19177c; }
code span.vs { color: #4070a0; }
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; }
</style>
<script>
// apply pandoc div.sourceCode style to pre.sourceCode instead
(function() {
var sheets = document.styleSheets;
for (var i = 0; i < sheets.length; i++) {
if (sheets[i].ownerNode.dataset["origin"] !== "pandoc") continue;
try { var rules = sheets[i].cssRules; } catch (e) { continue; }
var j = 0;
while (j < rules.length) {
var rule = rules[j];
// check if there is a div.sourceCode rule
if (rule.type !== rule.STYLE_RULE || rule.selectorText !== "div.sourceCode") {
j++;
continue;
}
var style = rule.style.cssText;
// check if color or background-color is set
if (rule.style.color === '' && rule.style.backgroundColor === '') {
j++;
continue;
}
// replace div.sourceCode by a pre.sourceCode rule
sheets[i].deleteRule(j);
sheets[i].insertRule('pre.sourceCode{' + style + '}', j);
}
}
})();
</script>
<style type="text/css">body {
background-color: #fff;
margin: 1em auto;
max-width: 700px;
overflow: visible;
padding-left: 2em;
padding-right: 2em;
font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.35;
}
#TOC {
clear: both;
margin: 0 0 10px 10px;
padding: 4px;
width: 400px;
border: 1px solid #CCCCCC;
border-radius: 5px;
background-color: #f6f6f6;
font-size: 13px;
line-height: 1.3;
}
#TOC .toctitle {
font-weight: bold;
font-size: 15px;
margin-left: 5px;
}
#TOC ul {
padding-left: 40px;
margin-left: -1.5em;
margin-top: 5px;
margin-bottom: 5px;
}
#TOC ul ul {
margin-left: -2em;
}
#TOC li {
line-height: 16px;
}
table {
margin: 1em auto;
border-width: 1px;
border-color: #DDDDDD;
border-style: outset;
border-collapse: collapse;
}
table th {
border-width: 2px;
padding: 5px;
border-style: inset;
}
table td {
border-width: 1px;
border-style: inset;
line-height: 18px;
padding: 5px 5px;
}
table, table th, table td {
border-left-style: none;
border-right-style: none;
}
table thead, table tr.even {
background-color: #f7f7f7;
}
p {
margin: 0.5em 0;
}
blockquote {
background-color: #f6f6f6;
padding: 0.25em 0.75em;
}
hr {
border-style: solid;
border: none;
border-top: 1px solid #777;
margin: 28px 0;
}
dl {
margin-left: 0;
}
dl dd {
margin-bottom: 13px;
margin-left: 13px;
}
dl dt {
font-weight: bold;
}
ul {
margin-top: 0;
}
ul li {
list-style: circle outside;
}
ul ul {
margin-bottom: 0;
}
pre, code {
background-color: #f7f7f7;
border-radius: 3px;
color: #333;
white-space: pre-wrap;
}
pre {
border-radius: 3px;
margin: 5px 0px 10px 0px;
padding: 10px;
}
pre:not([class]) {
background-color: #f7f7f7;
}
code {
font-family: Consolas, Monaco, 'Courier New', monospace;
font-size: 85%;
}
p > code, li > code {
padding: 2px 0px;
}
div.figure {
text-align: center;
}
img {
background-color: #FFFFFF;
padding: 2px;
border: 1px solid #DDDDDD;
border-radius: 3px;
border: 1px solid #CCCCCC;
margin: 0 5px;
}
h1 {
margin-top: 0;
font-size: 35px;
line-height: 40px;
}
h2 {
border-bottom: 4px solid #f7f7f7;
padding-top: 10px;
padding-bottom: 2px;
font-size: 145%;
}
h3 {
border-bottom: 2px solid #f7f7f7;
padding-top: 10px;
font-size: 120%;
}
h4 {
border-bottom: 1px solid #f7f7f7;
margin-left: 8px;
font-size: 105%;
}
h5, h6 {
border-bottom: 1px solid #ccc;
font-size: 105%;
}
a {
color: #0033dd;
text-decoration: none;
}
a:hover {
color: #6666ff; }
a:visited {
color: #800080; }
a:visited:hover {
color: #BB00BB; }
a[href^="http:"] {
text-decoration: underline; }
a[href^="https:"] {
text-decoration: underline; }
code > span.kw { color: #555; font-weight: bold; }
code > span.dt { color: #902000; }
code > span.dv { color: #40a070; }
code > span.bn { color: #d14; }
code > span.fl { color: #d14; }
code > span.ch { color: #d14; }
code > span.st { color: #d14; }
code > span.co { color: #888888; font-style: italic; }
code > span.ot { color: #007020; }
code > span.al { color: #ff0000; font-weight: bold; }
code > span.fu { color: #900; font-weight: bold; }
code > span.er { color: #a61717; background-color: #e3d2d2; }
</style>
</head>
<body>
<h1 class="title toc-ignore">Overview of the sass R package</h1>
<h4 class="author">Carson Sievert</h4>
<h4 class="date">2024-03-15</h4>
<style>
pre {
border: 1px solid #eee;
}
pre.r {
background-color: #ffffff;
margin-bottom: -0.5em;
}
pre.r code {
background-color: #ffffff;
}
pre.css {
background-color: #f8f8f8;
border-radius: 0;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
</style>
<div id="why" class="section level2">
<h2>Why Sass?</h2>
<p><a href="https://sass-lang.com/">Sass</a> is the most widely used,
feature rich, and stable CSS extension language available. It has become
an essential tool in modern web development because of its ability to
<em>reduce complexity</em> and <em>increase composability</em> when
styling a website. For a basic example, suppose you want to use the same
color for multiple styling rules in your website (e.g., hyperlinks and
buttons). With CSS, youd have to repeat that color every time it is
used in styling rule, so in a large project, changing at a later point
can be tedious and error-prone.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb1-1"><a href="#cb1-1" tabindex="-1"></a>a {</span>
<span id="cb1-2"><a href="#cb1-2" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> <span class="cn">salmon</span><span class="op">;</span></span>
<span id="cb1-3"><a href="#cb1-3" tabindex="-1"></a>}</span>
<span id="cb1-4"><a href="#cb1-4" tabindex="-1"></a>button {</span>
<span id="cb1-5"><a href="#cb1-5" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="cn">salmon</span><span class="op">;</span></span>
<span id="cb1-6"><a href="#cb1-6" tabindex="-1"></a>}</span></code></pre></div>
<p>With Sass, you could store this color in a Sass variable and use it
in the styling rules to produce the same CSS, resulting in a single
entry point for the colors value. This simple Sass tool can make CSS
styling a lot easier to reason about, making styles a lot easier to
customize and maintain.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb2-1"><a href="#cb2-1" tabindex="-1"></a>$main-color<span class="in">:</span> salmon;</span>
<span id="cb2-2"><a href="#cb2-2" tabindex="-1"></a>a {</span>
<span id="cb2-3"><a href="#cb2-3" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> $<span class="dv">main-color</span><span class="op">;</span></span>
<span id="cb2-4"><a href="#cb2-4" tabindex="-1"></a>}</span>
<span id="cb2-5"><a href="#cb2-5" tabindex="-1"></a>button {</span>
<span id="cb2-6"><a href="#cb2-6" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> $<span class="dv">main-color</span><span class="op">;</span></span>
<span id="cb2-7"><a href="#cb2-7" tabindex="-1"></a>}</span></code></pre></div>
<p>Sass variables are not the only Sass tool useful for reducing
complexity. For Sass and <strong>sass</strong> newcomers, this vignette
first covers how to use basic Sass tools like <a href="#variables">variables</a>, <a href="#mixins">mixins</a>, and <a href="#functions">functions</a> in the <strong>sass</strong> R package.
After the basics, youll also learn how to write <a href="#layering">composable <strong>sass</strong></a> that allows users
to easily override your styling defaults and developers to <a href="#includes">include</a> your Sass in their styling projects. Youll
also learn how to control the CSS output (e.g., <a href="#compression">compress</a> and <a href="#caching">cache</a> it),
and how to use it in <a href="#shiny"><strong>shiny</strong></a> &amp;
<a href="#rmarkdown"><strong>rmarkdown</strong></a>.</p>
<p>By mastering these concepts, youll not only be able to leverage the
advantages of using Sass over CSS, but youll also have the basis needed
to develop R interfaces to Sass projects that allow users to easily
customize your styling templates <em>without any knowledge of
Sass/CSS</em>. For an example, see the <a href="https://github.com/rstudio/bslib"><strong>bslib</strong> R
package</a>, which provides a interface to <a href="https://getbootstrap.com/docs/4.0/getting-started/theming/">Bootstrap
Sass</a> through easy-to-use functions like
<code>bs_theme_add_variables()</code>.</p>
</div>
<div id="basics" class="section level2">
<h2>Sass Basics</h2>
<div id="variables" class="section level3">
<h3>Variables</h3>
<p><a href="https://sass-lang.com/guide#topic-2">Sass variables</a> are
a great mechanism for simplifying and exposing CSS logic to users. To
create a variable, assign a value (likely a CSS <a href="https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/CSS_basics">property
value</a>) to a name, then refer to it by name in downstream Sass code.
In this minimal example, we create a <code>body-bg</code> variable then
use it to generate a single <a href="https://sass-lang.com/documentation//style-rules">style rule</a>,
but as well see later, variables can also be used inside of other
arbitrary Sass code (e.g., functions, mixins, etc).</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb3-1"><a href="#cb3-1" tabindex="-1"></a><span class="fu">library</span>(sass)</span>
<span id="cb3-2"><a href="#cb3-2" tabindex="-1"></a>variable <span class="ot">&lt;-</span> <span class="st">&quot;$body-bg: red;&quot;</span></span>
<span id="cb3-3"><a href="#cb3-3" tabindex="-1"></a>rule <span class="ot">&lt;-</span> <span class="st">&quot;body { background-color: $body-bg; }&quot;</span></span>
<span id="cb3-4"><a href="#cb3-4" tabindex="-1"></a><span class="fu">sass</span>(<span class="at">input =</span> <span class="fu">list</span>(variable, rule))</span></code></pre></div>
<div class="sourceCode" id="cb4"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb4-1"><a href="#cb4-1" tabindex="-1"></a><span class="co">/* CSS */</span></span>
<span id="cb4-2"><a href="#cb4-2" tabindex="-1"></a>body { </span>
<span id="cb4-3"><a href="#cb4-3" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="cn">red</span><span class="op">;</span></span>
<span id="cb4-4"><a href="#cb4-4" tabindex="-1"></a>}</span></code></pre></div>
<p>A more convenient and readable way to create Sass variables in R is
to use a named <code>list()</code>. Also, its a good idea to add the
<code>!default</code> flag after the value since it provides users of
your Sass an opportunity to set their own value. Well learn more about
defaults in <a href="#layering">layering</a>, but for now, just note
that the <code>!default</code> flag says use this value only if that
variable isnt already defined:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb5-1"><a href="#cb5-1" tabindex="-1"></a>user_default <span class="ot">&lt;-</span> <span class="fu">list</span>(<span class="st">&quot;body-bg&quot;</span> <span class="ot">=</span> <span class="st">&quot;blue !default&quot;</span>)</span>
<span id="cb5-2"><a href="#cb5-2" tabindex="-1"></a>default <span class="ot">&lt;-</span> <span class="fu">list</span>(<span class="st">&quot;body-bg&quot;</span> <span class="ot">=</span> <span class="st">&quot;red !default&quot;</span>)</span>
<span id="cb5-3"><a href="#cb5-3" tabindex="-1"></a><span class="fu">sass</span>(<span class="at">input =</span> <span class="fu">list</span>(user_default, default, rule))</span></code></pre></div>
<div class="sourceCode" id="cb6"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb6-1"><a href="#cb6-1" tabindex="-1"></a><span class="co">/* CSS */</span></span>
<span id="cb6-2"><a href="#cb6-2" tabindex="-1"></a>body { </span>
<span id="cb6-3"><a href="#cb6-3" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="cn">blue</span><span class="op">;</span></span>
<span id="cb6-4"><a href="#cb6-4" tabindex="-1"></a>}</span></code></pre></div>
</div>
<div id="functions" class="section level3">
<h3>Functions</h3>
<p>Sass comes with a variety of <a href="https://sass-lang.com/documentation//modules">built-in
functions</a> (i.e., you dont have to <a href="#imports">import</a>
anything to start using them) which are useful for working with CSS
values (<a href="https://sass-lang.com/documentation//modules/color">colors</a>, <a href="https://sass-lang.com/documentation//modules/math">numbers</a>, <a href="https://sass-lang.com/documentation//modules/string">strings</a>,
etc). These built-in functions are primarily useful modifying or
combining CSS values in such a way that isnt possible with CSS. Here we
use the <code>rgba()</code> to add alpha blending to <code>black</code>
and assign the result to a variable.<a href="#fn1" class="footnote-ref" id="fnref1"><sup>1</sup></a></p>
<div class="sourceCode" id="cb7"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb7-1"><a href="#cb7-1" tabindex="-1"></a>variable <span class="ot">&lt;-</span> <span class="fu">list</span>(<span class="st">&quot;body-bg&quot;</span> <span class="ot">=</span> <span class="st">&quot;rgba(black, 0.8)&quot;</span>)</span>
<span id="cb7-2"><a href="#cb7-2" tabindex="-1"></a><span class="fu">sass</span>(<span class="at">input =</span> <span class="fu">list</span>(variable, rule))</span></code></pre></div>
<div class="sourceCode" id="cb8"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb8-1"><a href="#cb8-1" tabindex="-1"></a><span class="co">/* CSS */</span></span>
<span id="cb8-2"><a href="#cb8-2" tabindex="-1"></a>body {</span>
<span id="cb8-3"><a href="#cb8-3" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="fu">rgba(</span><span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0.8</span><span class="fu">)</span><span class="op">;</span></span>
<span id="cb8-4"><a href="#cb8-4" tabindex="-1"></a>}</span></code></pre></div>
<p>Sass also provides the ability to define your own functions through
the <a href="https://sass-lang.com/documentation//at-rules/function"><code>@function</code>
at-rule</a>. Like functions in most languages, there are four main
components to a function definition: (1) the function <code>name</code>,
(2) the function argument/inputs (e.g., <code>arg1</code>,
<code>arg2</code>), (3) the function body which contains statements
(i.e., <code>statement1</code>, <code>statement2</code>, etc.), and
finally (4) a return <code>value</code>.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb9-1"><a href="#cb9-1" tabindex="-1"></a><span class="sc">@</span>function <span class="fu">name</span>(arg1, arg2) {</span>
<span id="cb9-2"><a href="#cb9-2" tabindex="-1"></a> statement1;</span>
<span id="cb9-3"><a href="#cb9-3" tabindex="-1"></a> statement2;</span>
<span id="cb9-4"><a href="#cb9-4" tabindex="-1"></a> <span class="sc">@</span>return value;</span>
<span id="cb9-5"><a href="#cb9-5" tabindex="-1"></a>}</span></code></pre></div>
<p><br></p>
<p>For an example of where creating your function becomes useful,
consider this <code>color-contrast()</code> function, inspired by <a href="https://stackoverflow.com/questions/3942878/how-to-decide-font-color-in-white-or-black-depending-on-background-color/3943023#3943023">this
SO answer</a> to a common problem that arises when allowing users
control over background color of something (e.g., the document body).
Wed like to strive for styling rules that are smart enough to overlay
white text on a dark colored background and black text on a light
colored background. <code>color-contrast()</code> helps us achieve this
since, given a dark color, it returns white; and given a light color, it
returns black.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb10-1"><a href="#cb10-1" tabindex="-1"></a><span class="sc">@</span>function color<span class="sc">-</span><span class="fu">contrast</span>(<span class="sc">$</span>color) {</span>
<span id="cb10-2"><a href="#cb10-2" tabindex="-1"></a> <span class="sc">@</span>return <span class="cf">if</span>(</span>
<span id="cb10-3"><a href="#cb10-3" tabindex="-1"></a> <span class="fu">red</span>(<span class="sc">$</span>color) <span class="sc">*</span> <span class="fl">0.299</span> <span class="sc">+</span> <span class="fu">green</span>(<span class="sc">$</span>color) <span class="sc">*</span> <span class="fl">0.587</span> <span class="sc">+</span> <span class="fu">blue</span>(<span class="sc">$</span>color) <span class="sc">*</span> <span class="fl">0.114</span> <span class="sc">&gt;</span> <span class="dv">186</span>,</span>
<span id="cb10-4"><a href="#cb10-4" tabindex="-1"></a> black, white</span>
<span id="cb10-5"><a href="#cb10-5" tabindex="-1"></a> );</span>
<span id="cb10-6"><a href="#cb10-6" tabindex="-1"></a>}</span></code></pre></div>
<p><br></p>
<p>By saving this function to a file named
<code>color-contrast.scss</code>, it can then be <a href="#imports">imported</a> and used in the following way. For a live
example of this in action, consider <a href="https://gallery.shinyapps.io/sass-font/">this Shiny app</a> which
allows the user to interactively choose a background color and the
titles font color automatically updates to an appropriate color
contrast. <a href="#shiny-dynamic">See here</a> for more on allowing
<strong>shiny</strong> users to influence styling on the page using
<strong>sass</strong>.</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb11-1"><a href="#cb11-1" tabindex="-1"></a><span class="fu">sass</span>(</span>
<span id="cb11-2"><a href="#cb11-2" tabindex="-1"></a> <span class="fu">list</span>(</span>
<span id="cb11-3"><a href="#cb11-3" tabindex="-1"></a> variable,</span>
<span id="cb11-4"><a href="#cb11-4" tabindex="-1"></a> <span class="fu">sass_file</span>(<span class="st">&quot;color-contrast.scss&quot;</span>),</span>
<span id="cb11-5"><a href="#cb11-5" tabindex="-1"></a> <span class="st">&quot;body {</span></span>
<span id="cb11-6"><a href="#cb11-6" tabindex="-1"></a><span class="st"> background-color: $body-bg;</span></span>
<span id="cb11-7"><a href="#cb11-7" tabindex="-1"></a><span class="st"> color: color-contrast($body-bg);</span></span>
<span id="cb11-8"><a href="#cb11-8" tabindex="-1"></a><span class="st"> }&quot;</span></span>
<span id="cb11-9"><a href="#cb11-9" tabindex="-1"></a> )</span>
<span id="cb11-10"><a href="#cb11-10" tabindex="-1"></a>)</span></code></pre></div>
<div class="sourceCode" id="cb12"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb12-1"><a href="#cb12-1" tabindex="-1"></a><span class="co">/* CSS */</span></span>
<span id="cb12-2"><a href="#cb12-2" tabindex="-1"></a>body {</span>
<span id="cb12-3"><a href="#cb12-3" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="fu">rgba(</span><span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0.8</span><span class="fu">)</span><span class="op">;</span></span>
<span id="cb12-4"><a href="#cb12-4" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> <span class="cn">white</span><span class="op">;</span></span>
<span id="cb12-5"><a href="#cb12-5" tabindex="-1"></a>}</span></code></pre></div>
<p><strong>NOTE</strong>: <code>bslib::bs_theme()</code> provides its
own, more sophisticated, version of <code>color-contrast()</code> that
you can use like so:
<code>sass::sass_partial(&quot;body{color: color-contrast($body-bg)}&quot;, bs_theme())</code></p>
</div>
<div id="imports" class="section level3">
<h3>Importing</h3>
<p>In practice, youll want to write your Sass code in
<code>.scss</code> (<a href="https://sass-lang.com/documentation//syntax">or
<code>.sass</code></a>) files (instead of inside R strings). That way
you can leverage things like syntax highlighting in RStudio (or your
favorite IDE) and make it easier for others to import your Sass into
their workflow. For example, if I have <code>.scss</code> file in my
working directory, say <code>my-style.scss</code>, I can compile it this
way:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb13-1"><a href="#cb13-1" tabindex="-1"></a><span class="fu">sass</span>(<span class="fu">sass_file</span>(<span class="st">&quot;my-style.scss&quot;</span>))</span></code></pre></div>
<p><img src="data:image/png;base64,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
<div class="sourceCode" id="cb14"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb14-1"><a href="#cb14-1" tabindex="-1"></a><span class="co">/* CSS */</span></span>
<span id="cb14-2"><a href="#cb14-2" tabindex="-1"></a>body {</span>
<span id="cb14-3"><a href="#cb14-3" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="fu">rgba(</span><span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0.8</span><span class="fu">)</span><span class="op">;</span></span>
<span id="cb14-4"><a href="#cb14-4" tabindex="-1"></a>}</span></code></pre></div>
<p>This works because <code>sass_file()</code> uses
<code>sass_import()</code> to generate an <code>@import</code> at-rule.
If you visit the <a href="https://sass-lang.com/documentation//at-rules/import">docs for
<code>@import</code></a>, youll notice theres more you can do that
import local <code>.scss</code>, like import local or remote
<code>.css</code> files, import font files, and more. Note also that
<code>{sass}</code> also provides tools that make importing of local
font files easier see <code>font_google()</code> to learn more.</p>
</div>
</div>
<div id="file-import" class="section level2">
<h2>Font imports</h2>
<p>Importing font file(s) directly in Sass/CSS/HTML can be a headache to
implement. This is especially true if you want to serve font files so
that a custom font renders on any client machine, even if the client is
without an internet connection. To make this easier, <code>{sass}</code>
provides a <code>font_google()</code> which can be used to download,
cache, import, and serve the relevant font files all at once.</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb15-1"><a href="#cb15-1" tabindex="-1"></a><span class="fu">library</span>(htmltools)</span>
<span id="cb15-2"><a href="#cb15-2" tabindex="-1"></a></span>
<span id="cb15-3"><a href="#cb15-3" tabindex="-1"></a>my_font <span class="ot">&lt;-</span> <span class="fu">list</span>(<span class="st">&quot;my-font&quot;</span> <span class="ot">=</span> <span class="fu">font_google</span>(<span class="st">&quot;Pacifico&quot;</span>))</span>
<span id="cb15-4"><a href="#cb15-4" tabindex="-1"></a>css <span class="ot">&lt;-</span> <span class="fu">sass</span>(</span>
<span id="cb15-5"><a href="#cb15-5" tabindex="-1"></a> <span class="fu">list</span>(</span>
<span id="cb15-6"><a href="#cb15-6" tabindex="-1"></a> my_font,</span>
<span id="cb15-7"><a href="#cb15-7" tabindex="-1"></a> <span class="fu">list</span>(<span class="st">&quot;body {font-family: $my-font}&quot;</span>)</span>
<span id="cb15-8"><a href="#cb15-8" tabindex="-1"></a> )</span>
<span id="cb15-9"><a href="#cb15-9" tabindex="-1"></a>)</span>
<span id="cb15-10"><a href="#cb15-10" tabindex="-1"></a></span>
<span id="cb15-11"><a href="#cb15-11" tabindex="-1"></a><span class="fu">shinyApp</span>(</span>
<span id="cb15-12"><a href="#cb15-12" tabindex="-1"></a> <span class="fu">fluidPage</span>(</span>
<span id="cb15-13"><a href="#cb15-13" tabindex="-1"></a> <span class="st">&quot;Hello&quot;</span>,</span>
<span id="cb15-14"><a href="#cb15-14" tabindex="-1"></a> tags<span class="sc">$</span><span class="fu">style</span>(css)</span>
<span id="cb15-15"><a href="#cb15-15" tabindex="-1"></a> ), </span>
<span id="cb15-16"><a href="#cb15-16" tabindex="-1"></a> <span class="cf">function</span>(...) {}</span>
<span id="cb15-17"><a href="#cb15-17" tabindex="-1"></a>)</span></code></pre></div>
<p><img src="data:image/png;base64,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
<p>To import non-Google fonts, use either <code>font_link()</code> or
<code>font_face()</code>. The former is for importing fonts via a remote
URL whereas the former could be use to import any font locally (or
remotely).</p>
<div id="mixins" class="section level3">
<h3>Mixins</h3>
<p>Similar to how functions are useful for encapsulating
<em>computation</em> in a reusable unit, <a href="https://sass-lang.com/documentation//at-rules/mixin">mixins</a>
are useful for doing the same with <em>styling rules</em> (i.e.,
packaging them into a reusable unit). Technically speaking, mixins are
similar to functions in that they require a <code>name</code>, may have
arguments, as well as any number of statements. However, they differ in
that they require the return value to be a <a href="https://sass-lang.com/documentation//style-rules">style rule</a>,
and when called, need to be <code>@include</code>d in a larger style
rule in order to generate any CSS.</p>
<p>For some examples, please see the <a href="https://sass-lang.com/documentation//at-rules/mixin">Sass mixin
documentation</a>.</p>
</div>
<div id="more-basics" class="section level3">
<h3>More basics</h3>
<p>This vignette intentionally doesnt try to re-invent the existing and
wonderful <a href="https://sass-lang.com/documentation/">Sass
documentation</a>. There youll find many more useful things as you
start to write more Sass, such as <a href="https://sass-lang.com/documentation//at-rules/control">control
flow</a>, <a href="https://sass-lang.com/documentation//values/lists">lists</a>, <a href="https://sass-lang.com/documentation//values/maps">maps</a>, <a href="https://sass-lang.com/documentation//interpolation">interpolation</a>,
and more.</p>
</div>
</div>
<div id="layering" class="section level2">
<h2>Composable sass</h2>
<p>To make Sass code more composable with other Sass code (e.g.,
allowing others to change your variable defaults or import a function or
mixin youve defined), consider partitioning your Sass code into a
<code>sass::sass_layer()</code>. The main idea is to split your Sass
into 4 parts: <a href="#functions"><code>functions</code></a>,
<code>defaults</code> (i.e. <a href="#variables">variable defaults</a>),
<a href="#mixins"><code>mixins</code></a>, and <code>rules</code> (i.e.,
styling rules).</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb16-1"><a href="#cb16-1" tabindex="-1"></a>layer1 <span class="ot">&lt;-</span> <span class="fu">sass_layer</span>(</span>
<span id="cb16-2"><a href="#cb16-2" tabindex="-1"></a> <span class="at">functions =</span> <span class="fu">sass_file</span>(<span class="st">&quot;color-contrast.scss&quot;</span>),</span>
<span id="cb16-3"><a href="#cb16-3" tabindex="-1"></a> <span class="at">defaults =</span> <span class="fu">list</span>(<span class="st">&quot;body-bg&quot;</span> <span class="ot">=</span> <span class="st">&quot;black !default&quot;</span>),</span>
<span id="cb16-4"><a href="#cb16-4" tabindex="-1"></a> <span class="at">rules =</span> <span class="st">&quot;body{background-color: $body-bg; color: color-contrast($body-bg)}&quot;</span></span>
<span id="cb16-5"><a href="#cb16-5" tabindex="-1"></a>)</span>
<span id="cb16-6"><a href="#cb16-6" tabindex="-1"></a><span class="fu">as_sass</span>(layer1)</span></code></pre></div>
<div class="sourceCode" id="cb17"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb17-1"><a href="#cb17-1" tabindex="-1"></a><span class="co">/* CSS */</span></span>
<span id="cb17-2"><a href="#cb17-2" tabindex="-1"></a>body {</span>
<span id="cb17-3"><a href="#cb17-3" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="cn">black</span><span class="op">;</span></span>
<span id="cb17-4"><a href="#cb17-4" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> <span class="cn">white</span><span class="op">;</span></span>
<span id="cb17-5"><a href="#cb17-5" tabindex="-1"></a>}</span></code></pre></div>
<p>This allows downstream <code>sass_layer()</code>s to be
<code>sass_bundle()</code>d into a single layer, where
<code>defaults</code> in downstream layers are granted higher priority.
More specifically, this means:</p>
<ul>
<li><code>defaults</code> for <code>layer2</code> are placed
<em>before</em> <code>defaults</code> for <code>layer1</code>.
<ul>
<li>Allowing downstream Sass to override variable defaults in upstream
Sass.</li>
</ul></li>
<li><code>rules</code> for <code>layer2</code> are placed <em>after</em>
<code>rules</code> for <code>layer1</code>.
<ul>
<li>Allows downstream rules to take precedence over upstream rules (<a href="https://css-tricks.com/precedence-css-order-css-matters/">precedence</a>
matters when there are multiple rules with the same level of <a href="https://css-tricks.com/specifics-on-css-specificity/">specificity</a>).</li>
</ul></li>
</ul>
<div class="sourceCode" id="cb18"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb18-1"><a href="#cb18-1" tabindex="-1"></a>layer2 <span class="ot">&lt;-</span> <span class="fu">sass_layer</span>(</span>
<span id="cb18-2"><a href="#cb18-2" tabindex="-1"></a> <span class="at">defaults =</span> <span class="fu">list</span>(<span class="st">&quot;body-bg&quot;</span> <span class="ot">=</span> <span class="st">&quot;white !default&quot;</span>)</span>
<span id="cb18-3"><a href="#cb18-3" tabindex="-1"></a>)</span>
<span id="cb18-4"><a href="#cb18-4" tabindex="-1"></a><span class="fu">sass</span>(<span class="fu">sass_bundle</span>(layer1, layer2))</span></code></pre></div>
<div class="sourceCode" id="cb19"><pre class="sourceCode css"><code class="sourceCode css"><span id="cb19-1"><a href="#cb19-1" tabindex="-1"></a><span class="co">/* CSS */</span></span>
<span id="cb19-2"><a href="#cb19-2" tabindex="-1"></a>body {</span>
<span id="cb19-3"><a href="#cb19-3" tabindex="-1"></a> <span class="kw">background-color</span><span class="ch">:</span> <span class="cn">white</span><span class="op">;</span></span>
<span id="cb19-4"><a href="#cb19-4" tabindex="-1"></a> <span class="kw">color</span><span class="ch">:</span> <span class="cn">black</span><span class="op">;</span></span>
<span id="cb19-5"><a href="#cb19-5" tabindex="-1"></a>}</span></code></pre></div>
<div id="imports-relative" class="section level3">
<h3>Resolving relative imports</h3>
<p>Another problem that <code>sass_layer()</code> helps solve is that
sometimes your Sass code might want to <a href="#imports">import</a> a
local file using a relative path that <em>you</em> know how to resolve,
but not necessarily the person who eventually compiles your Sass. To
solve this issue, provide a named character vector to
<code>file_attachments</code>, pointing the relevant relative path(s) to
the appropriate absolute path(s). Heres a contrived example of how that
might look (<a href="https://github.com/rstudio/bslib/blob/9c973e9/R/layers.R#L75-L77">heres</a>
a more real example of using it in an R package).</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb20-1"><a href="#cb20-1" tabindex="-1"></a><span class="fu">sass_layer</span>(</span>
<span id="cb20-2"><a href="#cb20-2" tabindex="-1"></a> <span class="at">declarations =</span> <span class="st">&quot;@import url(&#39;fonts/Source_Sans_Pro_300.ttf&#39;)&quot;</span>,</span>
<span id="cb20-3"><a href="#cb20-3" tabindex="-1"></a> <span class="at">file_attachments =</span> <span class="fu">c</span>(</span>
<span id="cb20-4"><a href="#cb20-4" tabindex="-1"></a> <span class="at">fonts =</span> <span class="st">&#39;/full/path/to/my/local/fonts&#39;</span></span>
<span id="cb20-5"><a href="#cb20-5" tabindex="-1"></a> )</span>
<span id="cb20-6"><a href="#cb20-6" tabindex="-1"></a>)</span></code></pre></div>
</div>
<div id="html-dependencies" class="section level3">
<h3>Attaching HTML dependencies</h3>
<p>Another problem that <code>sass_layer()</code> helps solve is that
sometimes you want to attach other HTML dependencies to your Sass/CSS
(e.g., JavaScript, other CSS, etc). For this reason,
<code>sass_layer()</code> has a <code>html_deps</code> argument to which
you can provide <code>htmltools::htmlDependency()</code> objects.
<code>sass()</code> preserves these, as well as any other HTML
dependencies attached to its input, by including them in the return
value. This ensures that, when you include <code>sass()</code> in <a href="#rmarkdown"><strong>rmarkdown</strong></a> or <a href="#shiny-string"><strong>shiny</strong></a> those dependencies come
along for the ride.</p>
<p><strong>DISCLAIMER</strong>: If you want to use this feature
<em>and</em> include <a href="#shiny-file">CSS as a file in shiny</a>,
youll need to call <code>htmltools::htmlDependencies()</code> on the
return value of <code>sass()</code> to get the dependencies, then
include them in your user interface definition.</p>
</div>
</div>
<div id="options" class="section level2">
<h2>CSS output options</h2>
<p>The <code>sass()</code> function provides a few arguments for
controlling the CSS output it generates, including <code>output</code>,
<code>options</code>, and <code>cache_options</code>. The following
covers some of the most important options available.</p>
<div id="output-file" class="section level3">
<h3>Output to a file</h3>
<p>If the CSS generated from <code>sass()</code> can be useful in more
than one place, consider writing it to a file (instead of returning it
as a string). To write CSS to a file, give a suitable file path to
<code>sass()</code>s <code>output</code> argument.</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb21-1"><a href="#cb21-1" tabindex="-1"></a><span class="fu">sass</span>(</span>
<span id="cb21-2"><a href="#cb21-2" tabindex="-1"></a> <span class="fu">sass_file</span>(<span class="st">&quot;my-style.scss&quot;</span>),</span>
<span id="cb21-3"><a href="#cb21-3" tabindex="-1"></a> <span class="at">output =</span> <span class="st">&quot;my-style.css&quot;</span></span>
<span id="cb21-4"><a href="#cb21-4" tabindex="-1"></a>)</span></code></pre></div>
</div>
<div id="compression" class="section level3">
<h3>Compression</h3>
<p>By default, <code>sass()</code> outputs <code>&#39;expanded&#39;</code> CSS
meaning there are lots of white-space and line-breaks included to make
it more readable by humans. Computers dont need all those unnecessary
characters, so to speed up your page load time when you go to include
the CSS in <a href="#shiny"><strong>shiny</strong></a> or <a href="#rmarkdown"><strong>rmarkdown</strong></a>, consider removing them
altogether with <code>output_style = &quot;compressed&quot;</code>:</p>
<div class="sourceCode" id="cb22"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb22-1"><a href="#cb22-1" tabindex="-1"></a><span class="fu">sass</span>(</span>
<span id="cb22-2"><a href="#cb22-2" tabindex="-1"></a> <span class="fu">sass_file</span>(<span class="st">&quot;my-style.scss&quot;</span>),</span>
<span id="cb22-3"><a href="#cb22-3" tabindex="-1"></a> <span class="at">options =</span> <span class="fu">sass_options</span>(<span class="at">output_style =</span> <span class="st">&quot;compressed&quot;</span>)</span>
<span id="cb22-4"><a href="#cb22-4" tabindex="-1"></a>)</span></code></pre></div>
</div>
<div id="source-maps" class="section level3">
<h3>Source maps</h3>
<p>When compressing the CSS output, it can be useful to include a <a href="https://web.dev/articles/source-maps">source map</a> so that its
easier to inspect the CSS from the website. The easiest way to include a
source map is to set <code>source_map_embed = TRUE</code>:</p>
<div class="sourceCode" id="cb23"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb23-1"><a href="#cb23-1" tabindex="-1"></a><span class="fu">sass</span>(</span>
<span id="cb23-2"><a href="#cb23-2" tabindex="-1"></a> <span class="fu">sass_file</span>(<span class="st">&quot;my-style.scss&quot;</span>),</span>
<span id="cb23-3"><a href="#cb23-3" tabindex="-1"></a> <span class="at">options =</span> <span class="fu">sass_options</span>(</span>
<span id="cb23-4"><a href="#cb23-4" tabindex="-1"></a> <span class="at">output_style =</span> <span class="st">&quot;compressed&quot;</span>,</span>
<span id="cb23-5"><a href="#cb23-5" tabindex="-1"></a> <span class="at">source_map_embed =</span> <span class="cn">TRUE</span></span>
<span id="cb23-6"><a href="#cb23-6" tabindex="-1"></a> )</span>
<span id="cb23-7"><a href="#cb23-7" tabindex="-1"></a>)</span></code></pre></div>
</div>
<div id="caching" class="section level3">
<h3>Caching</h3>
<p>Sometimes calling <code>sass()</code> can be computationally
expensive, in which case, it can be useful to leverage its caching
capability. Caching is enabled by default, unless Shinys Developer Mode
(<code>shiny::devmode()</code>) is enabled. To explicitly enable
(disable), set <code>options(sass.cache = )</code> to <code>TRUE</code>
(or <code>FALSE</code>):</p>
<div class="sourceCode" id="cb24"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb24-1"><a href="#cb24-1" tabindex="-1"></a>withr<span class="sc">::</span><span class="fu">with_options</span>(</span>
<span id="cb24-2"><a href="#cb24-2" tabindex="-1"></a> <span class="fu">list</span>(<span class="at">sass.cache =</span> <span class="cn">TRUE</span>),</span>
<span id="cb24-3"><a href="#cb24-3" tabindex="-1"></a> <span class="fu">sass</span>(<span class="fu">sass_file</span>(<span class="st">&quot;my-style.scss&quot;</span>))</span>
<span id="cb24-4"><a href="#cb24-4" tabindex="-1"></a>)</span></code></pre></div>
<p>You can also configure the location, size, and age of file caching
via <code>sass_file_cache()</code>, which can be passed directly to a
<code>sass()</code> call:</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb25-1"><a href="#cb25-1" tabindex="-1"></a><span class="fu">sass</span>(</span>
<span id="cb25-2"><a href="#cb25-2" tabindex="-1"></a> <span class="fu">sass_file</span>(<span class="st">&quot;my-style.scss&quot;</span>),</span>
<span id="cb25-3"><a href="#cb25-3" tabindex="-1"></a> <span class="at">cache =</span> <span class="fu">sass_file_cache</span>(<span class="fu">getwd</span>(), <span class="at">max_size =</span> <span class="dv">100</span> <span class="sc">*</span> <span class="dv">1024</span><span class="sc">^</span><span class="dv">2</span>)</span>
<span id="cb25-4"><a href="#cb25-4" tabindex="-1"></a>)</span></code></pre></div>
<p>Or used with <code>sass_cache_set_dir()</code> to configure the file
cache globally:</p>
<div class="sourceCode" id="cb26"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb26-1"><a href="#cb26-1" tabindex="-1"></a><span class="fu">sass_cache_set_dir</span>(<span class="fu">getwd</span>(), <span class="fu">sass_file_cache</span>(<span class="fu">getwd</span>(), <span class="at">max_size =</span> <span class="dv">100</span> <span class="sc">*</span> <span class="dv">1024</span><span class="sc">^</span><span class="dv">2</span>))</span></code></pre></div>
<p>Note that the location of the file cache defaults to
<code>sass_cache_context_dir()</code>, which depends on the context in
which its running. When inside a Shiny app, the cache location is
relative to the apps directory so the cache can persist and be shared
across R processes. Otherwise, the context directory is a OS and package
specific caching directory.</p>
</div>
</div>
<div id="shiny" class="section level2">
<h2>In shiny</h2>
<p>There are two basic approaches to including the CSS that
<code>sass()</code> returns as HTML in your <strong>shiny</strong> app.
If youre curious, the official <a href="https://shiny.rstudio.com/articles/css.html">shiny article on
CSS</a> has more details with a couple different approaches. Regardless
of the approach, consider leveraging <a href="#compression">compressing</a> and <a href="#caching">caching</a>
the CSS output to make your app faster to load.</p>
<div id="shiny-string" class="section level3">
<h3>As a string</h3>
<p>The character string that <code>sass()</code> returns is already
marked as <code>HTML()</code><a href="#fn2" class="footnote-ref" id="fnref2"><sup>2</sup></a>, so to include it in your
<strong>shiny</strong> app, wrap it in a <code>&lt;style&gt;</code> tag.
Its not necessary to place this tag in the <code>&lt;head&gt;</code> of
the document, but its good practice:</p>
<div class="sourceCode" id="cb27"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb27-1"><a href="#cb27-1" tabindex="-1"></a><span class="fu">library</span>(shiny)</span>
<span id="cb27-2"><a href="#cb27-2" tabindex="-1"></a>css <span class="ot">&lt;-</span> <span class="fu">sass</span>(<span class="fu">sass_file</span>(<span class="st">&quot;my-style.scss&quot;</span>))</span>
<span id="cb27-3"><a href="#cb27-3" tabindex="-1"></a><span class="fu">fluidPage</span>(</span>
<span id="cb27-4"><a href="#cb27-4" tabindex="-1"></a> tags<span class="sc">$</span><span class="fu">head</span>(tags<span class="sc">$</span><span class="fu">style</span>(css),</span>
<span id="cb27-5"><a href="#cb27-5" tabindex="-1"></a> ...</span>
<span id="cb27-6"><a href="#cb27-6" tabindex="-1"></a>)</span></code></pre></div>
</div>
<div id="shiny-file" class="section level3">
<h3>As a file</h3>
<p>To write CSS to a file, give a suitable file path to
<code>sass()</code>s <code>output</code> argument. Here we write to a
specially named <code>www/</code> subdirectory so that
<strong>shiny</strong> will automatically make those file(s) available
to the web app.</p>
<div class="sourceCode" id="cb28"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb28-1"><a href="#cb28-1" tabindex="-1"></a><span class="fu">library</span>(shiny)</span>
<span id="cb28-2"><a href="#cb28-2" tabindex="-1"></a><span class="fu">sass</span>(</span>
<span id="cb28-3"><a href="#cb28-3" tabindex="-1"></a> <span class="fu">sass_file</span>(<span class="st">&quot;my-style.scss&quot;</span>),</span>
<span id="cb28-4"><a href="#cb28-4" tabindex="-1"></a> <span class="at">output =</span> <span class="st">&quot;www/my-style.css&quot;</span></span>
<span id="cb28-5"><a href="#cb28-5" tabindex="-1"></a>)</span>
<span id="cb28-6"><a href="#cb28-6" tabindex="-1"></a><span class="fu">fluidPage</span>(</span>
<span id="cb28-7"><a href="#cb28-7" tabindex="-1"></a> tags<span class="sc">$</span><span class="fu">head</span>(</span>
<span id="cb28-8"><a href="#cb28-8" tabindex="-1"></a> tags<span class="sc">$</span><span class="fu">link</span>(<span class="at">href =</span> <span class="st">&quot;my-style.css&quot;</span>, <span class="at">rel =</span> <span class="st">&quot;stylesheet&quot;</span>, <span class="at">type =</span> <span class="st">&quot;text/css&quot;</span>)</span>
<span id="cb28-9"><a href="#cb28-9" tabindex="-1"></a> ),</span>
<span id="cb28-10"><a href="#cb28-10" tabindex="-1"></a> ...</span>
<span id="cb28-11"><a href="#cb28-11" tabindex="-1"></a>)</span></code></pre></div>
</div>
<div id="shiny-dynamic" class="section level3">
<h3>As a dynamic input</h3>
<p>Sometimes its useful to allow users of your <strong>shiny</strong>
app to be able to influence your apps styling via
<strong>shiny</strong> input(s). One way this can be done is via <a href="https://shiny.rstudio.com/articles/dynamic-ui.html">dynamic
UI</a>, where you use <code>renderUI()</code>/<code>uiOutput()</code> to
dynamically insert <a href="#shiny-string">CSS as an HTML string</a>
whenever a relevant input changes. Be aware, however, that whenever you
allow dynamic user input to generate <code>HTML()</code>, youre leaving
yourself open to security vulnerabilites; so try to avoid it, and
<em>never</em> allow clients to enter free form <code>textInput()</code>
without any sort of sanitation of the user input.</p>
<p>Consider this basic example of using a <code>colourInput()</code>
widget (from the <strong>colourpicker</strong> package) to choose the
bodys background color, which triggers a call to
<code>sass()</code>:</p>
<div class="sourceCode" id="cb29"><pre class="sourceCode r"><code class="sourceCode r"><span id="cb29-1"><a href="#cb29-1" tabindex="-1"></a><span class="fu">library</span>(shiny)</span>
<span id="cb29-2"><a href="#cb29-2" tabindex="-1"></a></span>
<span id="cb29-3"><a href="#cb29-3" tabindex="-1"></a>ui <span class="ot">&lt;-</span> <span class="fu">fluidPage</span>(</span>
<span id="cb29-4"><a href="#cb29-4" tabindex="-1"></a> <span class="fu">headerPanel</span>(<span class="st">&quot;Sass Color Example&quot;</span>),</span>
<span id="cb29-5"><a href="#cb29-5" tabindex="-1"></a> colourpicker<span class="sc">::</span><span class="fu">colourInput</span>(<span class="st">&quot;color&quot;</span>, <span class="st">&quot;Background Color&quot;</span>, <span class="at">value =</span> <span class="st">&quot;#6498d2&quot;</span>, <span class="at">showColour =</span> <span class="st">&quot;text&quot;</span>),</span>
<span id="cb29-6"><a href="#cb29-6" tabindex="-1"></a> <span class="fu">uiOutput</span>(<span class="st">&quot;sass&quot;</span>)</span>
<span id="cb29-7"><a href="#cb29-7" tabindex="-1"></a>)</span>
<span id="cb29-8"><a href="#cb29-8" tabindex="-1"></a></span>
<span id="cb29-9"><a href="#cb29-9" tabindex="-1"></a>server <span class="ot">&lt;-</span> <span class="cf">function</span>(input, output) {</span>
<span id="cb29-10"><a href="#cb29-10" tabindex="-1"></a> output<span class="sc">$</span>sass <span class="ot">&lt;-</span> <span class="fu">renderUI</span>({</span>
<span id="cb29-11"><a href="#cb29-11" tabindex="-1"></a> tags<span class="sc">$</span><span class="fu">head</span>(tags<span class="sc">$</span><span class="fu">style</span>(<span class="fu">css</span>()))</span>
<span id="cb29-12"><a href="#cb29-12" tabindex="-1"></a> })</span>
<span id="cb29-13"><a href="#cb29-13" tabindex="-1"></a> css <span class="ot">&lt;-</span> <span class="fu">reactive</span>({</span>
<span id="cb29-14"><a href="#cb29-14" tabindex="-1"></a> sass<span class="sc">::</span><span class="fu">sass</span>(<span class="fu">list</span>(</span>
<span id="cb29-15"><a href="#cb29-15" tabindex="-1"></a> <span class="fu">list</span>(<span class="at">color =</span> input<span class="sc">$</span>color),</span>
<span id="cb29-16"><a href="#cb29-16" tabindex="-1"></a> <span class="st">&quot;body { background-color: $color; }&quot;</span></span>
<span id="cb29-17"><a href="#cb29-17" tabindex="-1"></a> ))</span>
<span id="cb29-18"><a href="#cb29-18" tabindex="-1"></a> })</span>
<span id="cb29-19"><a href="#cb29-19" tabindex="-1"></a>}</span>
<span id="cb29-20"><a href="#cb29-20" tabindex="-1"></a></span>
<span id="cb29-21"><a href="#cb29-21" tabindex="-1"></a><span class="fu">shinyApp</span>(ui, server)</span></code></pre></div>
<p><br></p>
<p><img src="data:image/gif;base64,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
<p>Below are a few more sophisticated examples of dynamic input:</p>
<ul>
<li>Font Color
<ul>
<li><a href="https://gallery.shinyapps.io/sass-font" class="uri">https://gallery.shinyapps.io/sass-font</a></li>
<li><code>shiny::runApp(system.file(&quot;sass-font&quot;, package = &quot;sass&quot;))</code></li>
</ul></li>
<li>Sizing
<ul>
<li><a href="https://gallery.shinyapps.io/sass-size" class="uri">https://gallery.shinyapps.io/sass-size</a></li>
<li><code>shiny::runApp(system.file(&quot;sass-size&quot;, package = &quot;sass&quot;))</code></li>
</ul></li>
<li>Themes
<ul>
<li><a href="https://gallery.shinyapps.io/sass-theme" class="uri">https://gallery.shinyapps.io/sass-theme</a></li>
<li><code>shiny::runApp(system.file(&quot;sass-theme&quot;, package = &quot;sass&quot;))</code></li>
</ul></li>
</ul>
</div>
</div>
<div id="rmarkdown" class="section level2">
<h2>In rmarkdown</h2>
<p><strong>knitr</strong> <a href="https://github.com/yihui/knitr/pull/1666">recently gained</a> a
Sass engine powered by the <strong>sass</strong> package. This means you
can write Sass code directly into a code <code>sass</code> chunk and the
resulting CSS will be included in the resulting document (The
<code>echo = FALSE</code> prevents the Sass code from being shown in the
resulting document).</p>
<pre><code>```{sass, echo = FALSE}
$body-bg: red;
body{
background-color: $body-bg;
}
```</code></pre>
<p>If you like to write R <strong>sass</strong> code instead, you can do
that as well, and by default it works similarly to the <code>sass</code>
engine (except that the sass-specific code chunk options will be
ignored, but you can specify those <a href="#options">options</a> via
<code>sass::sass()</code> instead).</p>
<pre><code>```{r, echo = FALSE}
sass(sass_file(&quot;my-style.scss&quot;))
```</code></pre>
<p>If syntax highlighting is enabled in your output document , its also
possible to display the generated CSS (instead of embedding it as HTML)
with syntax highlighting by setting the code chunk options
<code>class.output=&#39;css&#39;</code> and <code>comment=&#39;&#39;</code>.
Unfortunately, for some output formats, like
<code>rmarkdown::html_vignette()</code>, syntax hightlighting isnt
supported, but for output formats like
<code>rmarkdown::html_document()</code>, you can enable syntax
highlighting by setting the <code>highlight</code> parameter to a
non-default value (e.g., <code>tango</code>).</p>
<pre><code>```{r, class.output=&#39;css&#39;, comment=&#39;&#39;}
sass(sass_file(&quot;my-style.scss&quot;))
```</code></pre>
</div>
<div class="footnotes footnotes-end-of-document">
<hr />
<ol>
<li id="fn1"><p>Oh, by the way, the value of a variable may be an
expression.<a href="#fnref1" class="footnote-back">↩︎</a></p></li>
<li id="fn2"><p>Including <a href="#shiny-dynamic">dynamic user
input</a> directly as <code>HTML()</code>, especially with free form
inputs like <code>textInput()</code>, is a security vulnerability. Avoid
it if you can.<a href="#fnref2" class="footnote-back">↩︎</a></p></li>
</ol>
</div>
<!-- code folding -->
<!-- dynamically load mathjax for compatibility with self-contained -->
<script>
(function () {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://mathjax.rstudio.com/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML";
document.getElementsByTagName("head")[0].appendChild(script);
})();
</script>
</body>
</html>