diff --git a/_quarto_internal_scss_error.scss b/_quarto_internal_scss_error.scss new file mode 100644 index 0000000..1f91c5c --- /dev/null +++ b/_quarto_internal_scss_error.scss @@ -0,0 +1,2685 @@ +// quarto-scss-analysis-annotation { "quarto-version": "1.9.37" } + +// quarto-scss-analysis-annotation { "origin": "'use' section from format" } + + + +// quarto-scss-analysis-annotation { "origin": "'use' section from Quarto" } + +@use "sass:color" as quarto-color; +@use "sass:map" as quarto-map; +@use "sass:math" as quarto-math; + + +// this file is currently intentionally empty + + +// quarto-scss-analysis-annotation { "origin": "'use' section from user-defined SCSS" } + + + +// quarto-scss-analysis-annotation { "origin": "'functions' section from format" } + + + +// quarto-scss-analysis-annotation { "origin": "'functions' section from Quarto" } + +@function colorToRGB($color) { + @return "rgb(" + red($color) + ", " + green($color) + ", " + blue($color) + + ")"; +} + +@function colorToRGBA($color) { + @return "rgba(" + red($color) + ", " + green($color) + ", " + blue($color) + + ", " + alpha($color) + ")"; +} + +@function str-replace($string, $search, $replace: "") { + $index: str-index($string, $search); + @if $index { + @return str-slice($string, 1, $index - 1) + $replace + + str-replace( + str-slice($string, $index + str-length($search)), + $search, + $replace + ); + } + @return $string; +} + + +@function colorToRGB($color) { + @return "rgb(" + red($color) + ", " + green($color) + ", " + blue($color) + + ")"; +} + +@function tint-color($color, $weight) { + @return mix(white, $color, $weight); +} + +@function shade-color($color, $weight) { + @return mix(black, $color, $weight); +} + +@function shift-color($color, $weight) { + @return if( + $weight > 0, + shade-color($color, $weight), + tint-color($color, -$weight) + ); +} + + +// quarto-scss-analysis-annotation { "origin": "'functions' section from user-defined SCSS" } + + + +// quarto-scss-analysis-annotation { "origin": "Defaults from user-defined SCSS" } + +// Colors +$primary: #003F30; +$body-color: #003F30; +$body-bg: #fefefe; +$link-color: $primary; +// Fonts +$font-family-sans-serif: "Google Sans", sans-serif; +// $font-family-monospace: "Source Code Pro", monospace; + + +$code-block-bg: #f1f3f5 !default; +$btn-code-copy-color: #4B886B !default; +$btn-code-copy-color-active: #6ca973 !default; +$presentation-font-size-root: 2em !default; + +// quarto-scss-analysis-annotation { "origin": "Defaults from Quarto's SCSS" } + + +@import url(./fonts/source-sans-pro/source-sans-pro.css); + +// fonts +$font-family-sans-serif: "Source Sans Pro", Helvetica, sans-serif !default; +$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace !default; +$presentation-font-size-root: 40px !default; +$presentation-font-smaller: 0.7 !default; +$presentation-line-height: 1.3 !default; + +// Default variables which exist in bootstrap themes +// and are here to simplify the implementation of _brand.yml and +// user theming customization in general +$font-weight-base: 400 !default; +$code-font-size: 1em !default; +$font-family-monospace-block: $font-family-monospace !default; +$font-family-monospace-inline: $font-family-monospace !default; +$link-weight: $font-weight-base !default; +$link-color-bg: transparent !default; +$link-decoration: inherit !default; +$font-weight-monospace: $font-weight-base !default; +$font-weight-monospace-block: $font-weight-monospace !default; +$font-weight-monospace-inline: $font-weight-monospace !default; + +// main colors +$body-bg: #fff !default; +$body-color: #222 !default; +$text-muted: lighten($body-color, 30%) !default; + +// grey colors (like in bootstrap) +$gray-200: #e9ecef !default; +$gray-100: #f8f9fa !default; +$gray-900: #212529 !default; + +// link colors +$primary: #2a76dd !default; +$link-color: $primary !default; +$link-color-hover: lighten($link-color, 10%) !default; + +// selection colors +$selection-bg: lighten($link-color, 25%) !default; +$selection-color: $body-bg !default; + +// border colors +$border-color: lighten($body-color, 30%) !default; +$border-width: 1px !default; +$border-radius: 4px !default; + +// headings +$presentation-heading-font: $font-family-sans-serif !default; +$presentation-heading-color: $body-color !default; +$presentation-heading-line-height: 1.2 !default; +$presentation-heading-letter-spacing: normal !default; +$presentation-heading-text-transform: none !default; +$presentation-heading-text-shadow: none !default; +$presentation-h1-text-shadow: none !default; +$presentation-heading-font-weight: 600 !default; +$presentation-h1-font-size: 2.5em !default; +$presentation-h2-font-size: 1.6em !default; +$presentation-h3-font-size: 1.3em !default; +$presentation-h4-font-size: 1em !default; + +// margins +$presentation-block-margin: 12px !default; + +// text alignment +$presentation-slide-text-align: left !default; +$presentation-title-slide-text-align: center !default; +$reveal-slide-text-align: $presentation-slide-text-align !default; +$reveal-title-slide-text-align: $presentation-title-slide-text-align !default; + +// Lists +$presentation-list-bullet-color: $body-color !default; + +// code blocks +$code-block-bg: $body-bg !default; +$code-block-border-color: lighten($body-color, 60%) !default; +$code-block-font-size: $code-font-size * 0.55 !default; +$code-block-height: 500px !default; +$code-block-theme-dark-threshhold: 40% !default; +$code-block-line-height: $presentation-line-height !default; +$code-block-color: $body-color !default; + +// inline code +$code-inline-font-size: $code-font-size * 0.875 !default; +$code-color: var(--quarto-hl-fu-color) !default; +$code-bg: transparent !default; + +// tabset +$tabset-border-color: $code-block-border-color !default; + +// table +$table-border-color: $code-block-border-color !default; + +// input panel +$input-panel-border-color: $code-block-border-color !default; +$input-panel-border-width: $border-width !default; +$input-panel-border-radius: $border-radius !default; +$input-panel-bg: rgba(248, 249, 250, 1) !default; + +// Callouts +$callout-border-width: 0.3rem !default; +$callout-border-scale: 0% !default; +$callout-icon-scale: 10% !default; +$callout-margin-top: 1rem !default; +$callout-margin-bottom: 1rem !default; +$callout-color-note: #0d6efd !default; +$callout-color-tip: #198754 !default; +$callout-color-important: #dc3545 !default; +$callout-color-caution: #fd7e14 !default; +$callout-color-warning: #ffc107 !default; + +// alternate colors for when the background changes +$light-bg-text-color: #222 !default; +$dark-bg-text-color: #fff !default; +$light-bg-link-color: #2a76dd !default; +$dark-bg-link-color: #42affa !default; +$light-bg-code-color: #4758ab !default; +$dark-bg-code-color: #ffa07a !default; + +// KBD variables +$kbd-padding-y: 0.4rem !default; +$kbd-padding-x: 0.4rem !default; +$kbd-font-size: $presentation-font-size-root !default; +$kbd-color: $body-color !default; +$kbd-bg: $gray-100 !default; // like in bootstrap style + +// --- derive reveal versions of presentation variables for finer-grained override --- + +$revealjs-font-size-root: $presentation-font-size-root !default; +$revealjs-h1-font-size: $presentation-h1-font-size !default; +$revealjs-h2-font-size: $presentation-h2-font-size !default; +$revealjs-h3-font-size: $presentation-h3-font-size !default; +$revealjs-h4-font-size: $presentation-h4-font-size !default; +$revealjs-heading-font: $presentation-heading-font !default; +$revealjs-heading-color: $presentation-heading-color !default; +$revealjs-heading-line-height: $presentation-heading-line-height !default; +$revealjs-heading-letter-spacing: $presentation-heading-letter-spacing !default; +$revealjs-heading-text-transform: $presentation-heading-text-transform !default; +$revealjs-heading-text-shadow: $presentation-heading-text-shadow !default; +$revealjs-h1-text-shadow: $presentation-h1-text-shadow !default; + +$revealjs-heading-font-weight: $presentation-heading-font-weight !default; +$revealjs-block-margin: $presentation-block-margin !default; +$revealjs-line-height: $presentation-line-height !default; +$revealjs-list-bullet-color: $presentation-list-bullet-color !default; + +$revealjs-code-inline-font-size: $code-inline-font-size !default; +$revealjs-code-block-font-size: $code-block-font-size !default; + +// ---- map to reveal scss variables --- +// ---- This is based from the revealjs setting.scss +// -- START setting.scss -- +// Background of the presentation +$backgroundColor: $body-bg !default; + +// Primary/body text +$mainFont: $font-family-sans-serif !default; +$mainFontSize: $revealjs-font-size-root !default; +$mainColor: $body-color !default; + +// Vertical spacing between blocks of text +$blockMargin: $revealjs-block-margin !default; + +// Headings +$headingMargin: 0 0 $blockMargin 0 !default; +$headingFont: $revealjs-heading-font !default; +$headingColor: $revealjs-heading-color !default; +$headingLineHeight: $revealjs-heading-line-height !default; +$headingLetterSpacing: $revealjs-heading-letter-spacing !default; +$headingTextTransform: $revealjs-heading-text-transform !default; +$headingTextShadow: $revealjs-heading-text-shadow !default; +$heading1TextShadow: $revealjs-h1-text-shadow !default; +$headingFontWeight: $revealjs-heading-font-weight !default; + +$heading1Size: $revealjs-h1-font-size !default; +$heading2Size: $revealjs-h2-font-size !default; +$heading3Size: $revealjs-h3-font-size !default; +$heading4Size: $revealjs-h4-font-size !default; + +$codeFont: $font-family-monospace !default; + +// Links and actions +$linkColor: $link-color !default; +$linkColorHover: $link-color-hover !default; + +// Text selection +$selectionBackgroundColor: $selection-bg !default; +$selectionColor: $selection-color !default; + +// Colors used for UI elements that are overlaid on top of +// the presentation +$overlayElementBgColor: 240, 240, 240 !default; +$overlayElementFgColor: 0, 0, 0 !default; + +// -- END setting.scss -- + +$code-copy-selector: "div.code-copy-outer-scaffold:hover > " !default; +$code-white-space: pre !default; +$tbl-cap-location: top !default; +// main colors +$body-bg: #fff !default; +$body-color: #222 !default; +$text-muted: lighten($body-color, 30%) !default; + +// border colors +$border-color: lighten($body-color, 30%) !default; +$table-border-color: $border-color !default; +$border-width: 1px !default; +$border-radius: 4px !default; + +// code block colors +$btn-code-copy-color: if( + variable-exists(text-muted), + $text-muted, + if(variable-exists(body-color), $body-color, $gray-900) +) !default; + +$btn-code-copy-color-active: if( + variable-exists(link-color), + $link-color, + #0d6efd +) !default; + + +// quarto-scss-analysis-annotation { "origin": "Defaults from the format SCSS" } + + + +// quarto-scss-analysis-annotation { "origin": "'mixins' section from format" } + +@mixin vertical-gradient( $top, $bottom ) { + background: $top; + background: -moz-linear-gradient( top, $top 0%, $bottom 100% ); + background: -webkit-gradient( linear, left top, left bottom, color-stop(0%,$top), color-stop(100%,$bottom) ); + background: -webkit-linear-gradient( top, $top 0%, $bottom 100% ); + background: -o-linear-gradient( top, $top 0%, $bottom 100% ); + background: -ms-linear-gradient( top, $top 0%, $bottom 100% ); + background: linear-gradient( top, $top 0%, $bottom 100% ); +} + +@mixin horizontal-gradient( $top, $bottom ) { + background: $top; + background: -moz-linear-gradient( left, $top 0%, $bottom 100% ); + background: -webkit-gradient( linear, left top, right top, color-stop(0%,$top), color-stop(100%,$bottom) ); + background: -webkit-linear-gradient( left, $top 0%, $bottom 100% ); + background: -o-linear-gradient( left, $top 0%, $bottom 100% ); + background: -ms-linear-gradient( left, $top 0%, $bottom 100% ); + background: linear-gradient( left, $top 0%, $bottom 100% ); +} + +@mixin radial-gradient( $outer, $inner, $type: circle ) { + background: $outer; + background: -moz-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: -webkit-gradient( radial, center center, 0px, center center, 100%, color-stop(0%,$inner), color-stop(100%,$outer) ); + background: -webkit-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: -o-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: -ms-radial-gradient( center, $type cover, $inner 0%, $outer 100% ); + background: radial-gradient( center, $type cover, $inner 0%, $outer 100% ); +} + +@mixin light-bg-text-color( $color ) { + section.has-light-background { + &, h1, h2, h3, h4, h5, h6 { + color: $color; + } + } +} + +@mixin dark-bg-text-color( $color ) { + section.has-dark-background { + &, h1, h2, h3, h4, h5, h6 { + color: $color; + } + } +} + +// quarto-scss-analysis-annotation { "origin": "'mixins' section from Quarto" } + + +@mixin shift_to_dark($property, $colorDark, $colorLight) { + @if ( + quarto-color.blackness($backgroundColor) > $code-block-theme-dark-threshhold + ) { + #{$property}: $colorDark; + } @else { + #{$property}: $colorLight; + } +} + +// Make the font size smaller by a factor of $times +// Useful for font-size defined in px inside smaller font size controled by em +// as they would not be impacted by the smaller font size +@mixin make-smaller-font-size($element, $times: 1) { + font-size: calc( + #{$element} * #{quarto-math.pow($presentation-font-smaller, $times)} + ); +} + +// Undo the smaller font size +// Useful for font-size in em already that should not be impacted by smaller font size controled by em +@mixin undo-smaller-font-size($element) { + font-size: calc(#{$element} / #{$presentation-font-smaller}); +} + +// -- START setting.scss -- + +// Generates the presentation background, can be overridden +// to return a background image or gradient +@mixin bodyBackground() { + background: $backgroundColor; +} + +// -- END setting.scss -- + + +// quarto-scss-analysis-annotation { "origin": "'mixins' section from user-defined SCSS" } + + + + +// quarto-scss-analysis-annotation { "origin": "'rules' section from format" } + +// Base theme template for reveal.js + +/********************************************* + * GLOBAL STYLES + *********************************************/ + +@import "./exposer"; + +.reveal-viewport { + @include bodyBackground(); + background-color: var(--r-background-color); +} + +.reveal { + font-family: var(--r-main-font); + font-size: var(--r-main-font-size); + font-weight: normal; + color: var(--r-main-color); +} + +.reveal ::selection { + color: var(--r-selection-color); + background: var(--r-selection-background-color); + text-shadow: none; +} + +.reveal ::-moz-selection { + color: var(--r-selection-color); + background: var(--r-selection-background-color); + text-shadow: none; +} + +.reveal .slides section, +.reveal .slides section>section { + line-height: 1.3; + font-weight: inherit; +} + +/********************************************* + * HEADERS + *********************************************/ + +.reveal h1, +.reveal h2, +.reveal h3, +.reveal h4, +.reveal h5, +.reveal h6 { + margin: var(--r-heading-margin); + color: var(--r-heading-color); + + font-family: var(--r-heading-font); + font-weight: var(--r-heading-font-weight); + line-height: var(--r-heading-line-height); + letter-spacing: var(--r-heading-letter-spacing); + + text-transform: var(--r-heading-text-transform); + text-shadow: var(--r-heading-text-shadow); + + word-wrap: break-word; +} + +.reveal h1 {font-size: var(--r-heading1-size); } +.reveal h2 {font-size: var(--r-heading2-size); } +.reveal h3 {font-size: var(--r-heading3-size); } +.reveal h4 {font-size: var(--r-heading4-size); } + +.reveal h1 { + text-shadow: var(--r-heading1-text-shadow); +} + + +/********************************************* + * OTHER + *********************************************/ + +.reveal p { + margin: var(--r-block-margin) 0; + line-height: 1.3; +} + +/* Remove trailing margins after titles */ +.reveal h1:last-child, +.reveal h2:last-child, +.reveal h3:last-child, +.reveal h4:last-child, +.reveal h5:last-child, +.reveal h6:last-child { + margin-bottom: 0; +} + +/* Ensure certain elements are never larger than the slide itself */ +.reveal img, +.reveal video, +.reveal iframe { + max-width: 95%; + max-height: 95%; +} +.reveal strong, +.reveal b { + font-weight: bold; +} + +.reveal em { + font-style: italic; +} + +.reveal ol, +.reveal dl, +.reveal ul { + display: inline-block; + + text-align: left; + margin: 0 0 0 1em; +} + +.reveal ol { + list-style-type: decimal; +} + +.reveal ul { + list-style-type: disc; +} + +.reveal ul ul { + list-style-type: square; +} + +.reveal ul ul ul { + list-style-type: circle; +} + +.reveal ul ul, +.reveal ul ol, +.reveal ol ol, +.reveal ol ul { + display: block; + margin-left: 40px; +} + +.reveal dt { + font-weight: bold; +} + +.reveal dd { + margin-left: 40px; +} + +.reveal blockquote { + display: block; + position: relative; + width: 70%; + margin: var(--r-block-margin) auto; + padding: 5px; + + font-style: italic; + background: rgba(255, 255, 255, 0.05); + box-shadow: 0px 0px 2px rgba(0,0,0,0.2); +} + .reveal blockquote p:first-child, + .reveal blockquote p:last-child { + display: inline-block; + } + +.reveal q { + font-style: italic; +} + +.reveal pre { + display: block; + position: relative; + width: 90%; + margin: var(--r-block-margin) auto; + + text-align: left; + font-size: 0.55em; + font-family: var(--r-code-font); + line-height: 1.2em; + + word-wrap: break-word; + + box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.15); +} + +.reveal code { + font-family: var(--r-code-font); + text-transform: none; + tab-size: 2; +} + +.reveal pre code { + display: block; + padding: 5px; + overflow: auto; + max-height: 400px; + word-wrap: normal; +} + +.reveal .code-wrapper { + white-space: normal; +} + +.reveal .code-wrapper code { + white-space: pre; +} + +.reveal table { + margin: auto; + border-collapse: collapse; + border-spacing: 0; +} + +.reveal table th { + font-weight: bold; +} + +.reveal table th, +.reveal table td { + text-align: left; + padding: 0.2em 0.5em 0.2em 0.5em; + border-bottom: 1px solid; +} + +.reveal table th[align="center"], +.reveal table td[align="center"] { + text-align: center; +} + +.reveal table th[align="right"], +.reveal table td[align="right"] { + text-align: right; +} + +.reveal table tbody tr:last-child th, +.reveal table tbody tr:last-child td { + border-bottom: none; +} + +.reveal sup { + vertical-align: super; + font-size: smaller; +} +.reveal sub { + vertical-align: sub; + font-size: smaller; +} + +.reveal small { + display: inline-block; + font-size: 0.6em; + line-height: 1.2em; + vertical-align: top; +} + +.reveal small * { + vertical-align: top; +} + +.reveal img { + margin: var(--r-block-margin) 0; +} + + +/********************************************* + * LINKS + *********************************************/ + +.reveal a { + color: var(--r-link-color); + text-decoration: none; + transition: color .15s ease; +} + .reveal a:hover { + color: var(--r-link-color-hover); + text-shadow: none; + border: none; + } + +.reveal .roll span:after { + color: #fff; + // background: darken( var(--r-link-color), 15% ); + background: var(--r-link-color-dark); + +} + + +/********************************************* + * Frame helper + *********************************************/ + +.reveal .r-frame { + border: 4px solid var(--r-main-color); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); +} + +.reveal a .r-frame { + transition: all .15s linear; +} + +.reveal a:hover .r-frame { + border-color: var(--r-link-color); + box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); +} + + +/********************************************* + * NAVIGATION CONTROLS + *********************************************/ + +.reveal .controls { + color: var(--r-link-color); +} + + +/********************************************* + * PROGRESS BAR + *********************************************/ + +.reveal .progress { + background: rgba(0,0,0,0.2); + color: var(--r-link-color); +} + +/********************************************* + * PRINT BACKGROUND + *********************************************/ + @media print { + .backgrounds { + background-color: var(--r-background-color); + } +} + + +// quarto-scss-analysis-annotation { "origin": "'rules' section from Quarto" } + +// floating + +.top-right { + position: absolute; + top: 1em; + right: 1em; +} + +// hidden + +// https://github.com/quarto-dev/quarto-cli/issues/5403#issuecomment-1533791947 +.visually-hidden { + border: 0; + clip: rect(0 0 0 0); + height: auto; + margin: 0; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; + white-space: nowrap; +} + +.hidden { + display: none !important; +} + +.zindex-bottom { + z-index: -1 !important; +} + +// layout and figures + +figure.figure { + display: block; +} + +.quarto-layout-panel { + margin-bottom: 1em; +} + +.quarto-layout-panel > figure { + width: 100%; +} +.quarto-layout-panel > figure > figcaption, +.quarto-layout-panel > .panel-caption { + margin-top: 10pt; +} + +.quarto-layout-panel > .table-caption { + margin-top: 0px; +} + +.table-caption p { + margin-bottom: 0.5em; +} + +.quarto-layout-row { + display: flex; + flex-direction: row; + align-items: flex-start; +} +.quarto-layout-valign-top { + align-items: flex-start; +} +.quarto-layout-valign-bottom { + align-items: flex-end; +} +.quarto-layout-valign-center { + align-items: center; +} +.quarto-layout-cell { + position: relative; + margin-right: 20px; +} +.quarto-layout-cell:last-child { + margin-right: 0; +} +.quarto-layout-cell figure, +.quarto-layout-cell > p { + margin: 0.2em; +} +.quarto-layout-cell img { + max-width: 100%; +} +.quarto-layout-cell .html-widget { + width: 100% !important; +} +.quarto-layout-cell div figure p { + margin: 0; +} +.quarto-layout-cell figure { + display: block; + margin-inline-start: 0; + margin-inline-end: 0; +} +.quarto-layout-cell table { + display: inline-table; +} +.quarto-layout-cell-subref figcaption, +figure .quarto-layout-row figure figcaption { + text-align: center; + font-style: italic; +} +.quarto-figure { + position: relative; + margin-bottom: 1em; +} + +.quarto-figure > figure { + width: 100%; + margin-bottom: 0; +} +.quarto-figure-left > figure > p, +.quarto-figure-left > figure > div /* for mermaid and dot diagrams */ { + text-align: left; +} +.quarto-figure-center > figure > p, +.quarto-figure-center > figure > div /* for mermaid and dot diagrams */ { + text-align: center; +} +.quarto-figure-right > figure > p, +.quarto-figure-right > figure > div /* for mermaid and dot diagrams */ { + text-align: right; +} + +.quarto-figure > figure > div.cell-annotation, +.quarto-figure > figure > div code { + text-align: left; /* override align center for code blocks */ +} + +figure > p:empty { + display: none; +} +figure > p:first-child { + margin-top: 0; + margin-bottom: 0; +} + +figure > figcaption.quarto-float-caption-bottom { + margin-bottom: 0.5em; +} +figure > figcaption.quarto-float-caption-top { + margin-top: 0.5em; +} + +// anchor + +// anchor js + +div[id^="tbl-"] { + position: relative; +} + +.quarto-figure > .anchorjs-link { + position: absolute; + top: 0.6em; + right: 0.5em; +} + +div[id^="tbl-"] > .anchorjs-link { + position: absolute; + top: 0.7em; + right: 0.3em; +} + +/* workaround for anchorjs not hitting on generic :hover selector */ +.quarto-figure:hover > .anchorjs-link, +div[id^="tbl-"]:hover > .anchorjs-link, +h2:hover > .anchorjs-link, +h3:hover > .anchorjs-link, +h4:hover > .anchorjs-link, +h5:hover > .anchorjs-link, +h6:hover > .anchorjs-link, +.reveal-anchorjs-link > .anchorjs-link { + opacity: 1; +} + +#title-block-header { + margin-block-end: 1rem; + position: relative; + margin-top: -1px; // Chrome draws 1px white line between navbar and title block +} + +#title-block-header .abstract { + margin-block-start: 1rem; +} + +#title-block-header .abstract .abstract-title { + font-weight: 600; +} + +#title-block-header a { + text-decoration: none; +} + +#title-block-header .author, +#title-block-header .date, +#title-block-header .doi { + margin-block-end: 0.2rem; +} + +#title-block-header .quarto-title-block > div { + display: flex; +} + +#title-block-header .quarto-title-block > div > h1 { + flex-grow: 1; +} + +#title-block-header .quarto-title-block > div > button { + flex-shrink: 0; + height: 2.25rem; + margin-top: 0; +} + +#title-block-header .quarto-title-block > div > button { + @if mixin-exists(media-breakpoint-up) { + @include media-breakpoint-up(lg) { + margin-top: 5px; + } + } +} + +// (Remove bottom margin from paragraphs in table headers) +tr.header > th > p:last-of-type { + margin-bottom: 0px; +} + +table, +table.table { + margin-top: 0.5rem; + margin-bottom: 0.5rem; +} + +caption, +.table-caption { + padding-top: 0.5rem; + padding-bottom: 0.5rem; + text-align: center; +} + +figure.quarto-float-tbl figcaption.quarto-float-caption-top { + margin-top: 0.5rem; + margin-bottom: 0.25rem; + text-align: center; +} + +figure.quarto-float-tbl figcaption.quarto-float-caption-bottom { + padding-top: 0.25rem; + margin-bottom: 0.5rem; + text-align: center; +} + +// utterances +.utterances { + max-width: none; + margin-left: -8px; +} + +// iframe +iframe { + margin-bottom: 1em; +} + +// details +details { + margin-bottom: 1em; +} +details[show] { + margin-bottom: 0; +} + +details > summary { + @if variable-exists(text-muted) { + color: $text-muted; + } +} + +details > summary > p:only-child { + display: inline; +} + +// codeCopy +div.code-copy-outer-scaffold { + position: relative; +} + +// Inline code should wrap +// See https://github.com/quarto-dev/quarto-cli/issues/2649 +dd code:not(.sourceCode), +p code:not(.sourceCode) { + white-space: pre-wrap; +} + +// default to scrolling output rather than wrapping, since +// multi-column tabular output (very common for R & Python) is +// unreadable when wrapped. +code { + white-space: pre; +} +@media print { + code { + white-space: pre-wrap; + } +} +pre > code { + display: block; +} + +pre > code.sourceCode { + white-space: $code-white-space; +} + +pre > code.sourceCode > span > a:first-child::before { + text-decoration: none; +} + +pre.code-overflow-wrap > code.sourceCode { + white-space: pre-wrap; +} + +pre.code-overflow-scroll > code.sourceCode { + white-space: pre; +} + +// code linking (pkgdown style) +code a:any-link { + color: inherit; + text-decoration: none; +} +code a:hover { + color: inherit; + text-decoration: underline; +} + +// task lists +ul.task-list { + padding-left: 1em; +} + +// tippy + +[data-tippy-root] { + display: inline-block; +} + +.tippy-content .footnote-back { + display: none; +} + +.footnote-back { + margin-left: 0.2em; +} + +.tippy-content { + overflow-x: auto; +} + +// embedded source code +.quarto-embedded-source-code { + display: none; +} + +// unresolved crossrefs +.quarto-unresolved-ref { + font-weight: 600; +} + +// html cover image injection +.quarto-cover-image { + max-width: 35%; + float: right; + margin-left: 30px; +} + +// provide margin below jupyter widgets +.cell-output-display .widget-subarea { + margin-bottom: 1em; +} + +// fix for selectize inputs getting their contents clipped +// this also works for knitr sql cells (see github issue #3497) +.cell-output-display:not(.no-overflow-x), +.knitsql-table:not(.no-overflow-x) { + overflow-x: auto; +} + +.panel-input { + margin-bottom: 1em; +} + +.panel-input > div, +.panel-input > div > div { + display: inline-block; + vertical-align: top; + padding-right: 12px; +} + +.panel-input > p:last-child { + margin-bottom: 0; +} + +.layout-sidebar { + margin-bottom: 1em; +} + +.layout-sidebar .tab-content { + border: none; +} + +.tab-content > .page-columns.active { + display: grid; +} + +// default styling for .code-preview=".." iframes +div.sourceCode > iframe { + width: 100%; + height: 300px; + + // this negative-margin hack works around the rendering issue with + // iframes and parent elements with rounded corners if the border + // radius for (eg) div.sourceCode is changed, this is likely going + // to need changing as well. + @if variable-exists(code-preview-margin-bottom) { + margin-bottom: $code-preview-margin-bottom; + } @else { + margin-bottom: -0.5em; + } + @if variable-exists(code-preview-border-color) { + border: $code-preview-border-color; + } +} + +// link styling +a { + text-underline-offset: 3px; +} + +/* Callout styling */ + +.callout pre.sourceCode { + padding-left: 0; +} + +// ansi escaping +div.ansi-escaped-output { + font-family: monospace; + display: block; +} + +/*! +* +* ansi colors from IPython notebook's +* +* we also add `bright-[color]-` synonyms for the `-[color]-intense` classes since +* that seems to be what ansi_up emits +* +*/ +/* CSS font colors for translated ANSI escape sequences */ +/* The color values are a mix of + http://www.xcolors.net/dl/baskerville-ivorylight and + http://www.xcolors.net/dl/euphrasia */ +.ansi-black-fg { + color: #3e424d; +} +.ansi-black-bg { + background-color: #3e424d; +} +.ansi-black-intense-black, +.ansi-bright-black-fg { + color: #282c36; +} +.ansi-black-intense-black, +.ansi-bright-black-bg { + background-color: #282c36; +} +.ansi-red-fg { + color: #e75c58; +} +.ansi-red-bg { + background-color: #e75c58; +} +.ansi-red-intense-red, +.ansi-bright-red-fg { + color: #b22b31; +} +.ansi-red-intense-red, +.ansi-bright-red-bg { + background-color: #b22b31; +} +.ansi-green-fg { + color: #00a250; +} +.ansi-green-bg { + background-color: #00a250; +} +.ansi-green-intense-green, +.ansi-bright-green-fg { + color: #007427; +} +.ansi-green-intense-green, +.ansi-bright-green-bg { + background-color: #007427; +} +.ansi-yellow-fg { + color: #ddb62b; +} +.ansi-yellow-bg { + background-color: #ddb62b; +} +.ansi-yellow-intense-yellow, +.ansi-bright-yellow-fg { + color: #b27d12; +} +.ansi-yellow-intense-yellow, +.ansi-bright-yellow-bg { + background-color: #b27d12; +} +.ansi-blue-fg { + color: #208ffb; +} +.ansi-blue-bg { + background-color: #208ffb; +} +.ansi-blue-intense-blue, +.ansi-bright-blue-fg { + color: #0065ca; +} +.ansi-blue-intense-blue, +.ansi-bright-blue-bg { + background-color: #0065ca; +} +.ansi-magenta-fg { + color: #d160c4; +} +.ansi-magenta-bg { + background-color: #d160c4; +} +.ansi-magenta-intense-magenta, +.ansi-bright-magenta-fg { + color: #a03196; +} +.ansi-magenta-intense-magenta, +.ansi-bright-magenta-bg { + background-color: #a03196; +} +.ansi-cyan-fg { + color: #60c6c8; +} +.ansi-cyan-bg { + background-color: #60c6c8; +} +.ansi-cyan-intense-cyan, +.ansi-bright-cyan-fg { + color: #258f8f; +} +.ansi-cyan-intense-cyan, +.ansi-bright-cyan-bg { + background-color: #258f8f; +} +.ansi-white-fg { + color: #c5c1b4; +} +.ansi-white-bg { + background-color: #c5c1b4; +} +.ansi-white-intense-white, +.ansi-bright-white-fg { + color: #a1a6b2; +} +.ansi-white-intense-white, +.ansi-bright-white-bg { + background-color: #a1a6b2; +} +.ansi-default-inverse-fg { + color: #ffffff; +} +.ansi-default-inverse-bg { + background-color: #000000; +} +.ansi-bold { + font-weight: bold; +} +.ansi-underline { + text-decoration: underline; +} + +:root { + --quarto-body-bg: #{$body-bg}; + --quarto-body-color: #{$body-color}; + --quarto-text-muted: #{$text-muted}; + --quarto-border-color: #{$table-border-color}; + --quarto-border-width: #{$border-width}; + @if not variable-exists(enable-rounded) or $enable-rounded == true { + --quarto-border-radius: #{$border-radius}; + } +} + +/* rules to support GT table styling */ +table.gt_table { + color: var(--quarto-body-color); + font-size: 1em; + width: 100%; // to match other table styling + background-color: transparent; + border-top-width: inherit; + border-bottom-width: inherit; + border-color: var(--quarto-border-color); +} + +table.gt_table th.gt_column_spanner_outer { + color: var(--quarto-body-color); + background-color: transparent; + border-top-width: inherit; + border-bottom-width: inherit; + border-color: var(--quarto-border-color); +} + +table.gt_table th.gt_col_heading { + color: var(--quarto-body-color); + font-weight: bold; + background-color: transparent; +} + +table.gt_table thead.gt_col_headings { + border-bottom: 1px solid currentColor; + border-top-width: inherit; + border-top-color: var(--quarto-border-color); +} + +table.gt_table thead.gt_col_headings:not(:first-child) { + border-top-width: 1px; + border-top-color: var(--quarto-border-color); +} + +table.gt_table td.gt_row { + border-bottom-width: 1px; + border-bottom-color: var(--quarto-border-color); + border-top-width: 0px; +} + +table.gt_table tbody.gt_table_body { + border-top-width: 1px; + border-bottom-width: 1px; + border-bottom-color: var(--quarto-border-color); + border-top-color: currentColor; +} + +/* restore previous pandoc columns behavior + (too many reports of slide layout breaking) + see https://github.com/jgm/pandoc/pull/8237 +*/ +div.columns { + display: initial; + gap: initial; +} +div.column { + display: inline-block; + overflow-x: initial; + vertical-align: top; + width: 50%; +} + +// Code Annotation LayoutBoot +.code-annotation-tip-content { + word-wrap: break-word; +} + +.code-annotation-container-hidden { + display: none !important; +} + +dl.code-annotation-container-grid { + display: grid; + grid-template-columns: min-content auto; + dt { + grid-column: 1; + } + dd { + grid-column: 2; + } +} + +pre.sourceCode.code-annotation-code { + padding-right: 0; +} + +code.sourceCode .code-annotation-anchor { + z-index: 100; + position: relative; + float: right; + background-color: transparent; +} + +// Add a bit of margin to the right of a checkbox +// https://github.com/quarto-dev/quarto-cli/issues/6627 +input[type="checkbox"] { + margin-right: 0.5ch; +} + +// Mermaid Theming +// if none come from theme, we need these +$body-color: #222 !default; +$body-bg: #fff !default; +$primary: #468 !default; +$secondary: #999 !default; +$font-family-sans-serif: sans-serif !default; +$font-weight-base: 400 !default; + +/* SCSS variables + + These are documented in quarto-cli/quarto-web:docs/authoring/_mermaid-theming.qmd + + Make sure to update the docs if you change these. +*/ +$mermaid-bg-color: $body-bg !default; +$mermaid-edge-color: $secondary !default; +$mermaid-node-fg-color: $body-color !default; +$mermaid-fg-color: $body-color !default; +$mermaid-fg-color--lighter: lighten($body-color, 10%) !default; +$mermaid-fg-color--lightest: lighten($body-color, 20%) !default; +$mermaid-font-family: $font-family-sans-serif !default; +$mermaid-font-weight: $font-weight-base !default; +$mermaid-label-bg-color: $body-bg !default; +$mermaid-label-fg-color: $primary !default; +$mermaid-node-bg-color: rgba($primary, 0.1) !default; +$mermaid-node-fg-color: $primary !default; + +/* CSS variables */ +:root { + --mermaid-bg-color: #{$mermaid-bg-color}; + --mermaid-edge-color: #{$mermaid-edge-color}; + --mermaid-node-fg-color: #{$mermaid-node-fg-color}; + --mermaid-fg-color: #{$mermaid-fg-color}; + --mermaid-fg-color--lighter: #{$mermaid-fg-color--lighter}; + --mermaid-fg-color--lightest: #{$mermaid-fg-color--lightest}; + --mermaid-font-family: #{$mermaid-font-family}; + --mermaid-label-bg-color: #{$mermaid-label-bg-color}; + --mermaid-label-fg-color: #{$mermaid-label-fg-color}; + --mermaid-node-bg-color: #{$mermaid-node-bg-color}; + --mermaid-node-fg-color: #{$mermaid-node-fg-color}; +} + +@media print { + :root { + font-size: 11pt; + } + #quarto-sidebar, + #TOC, + .nav-page { + display: none; + } + .page-columns .content { + grid-column-start: page-start; + } + .fixed-top { + position: relative; + } + .panel-caption, + .figure-caption, + figcaption { + color: #666; + } +} + +// light and dark content + +body.quarto-light .dark-content { + display: none !important; +} + +body.quarto-dark .light-content { + display: none !important; +} + +.code-copy-button { + position: absolute; + top: 0; + right: 0; + border: 0; + margin-top: 5px; + margin-right: 5px; + background-color: transparent; + z-index: 3; +} + +.code-copy-button-tooltip { + font-size: 0.75em; +} + +#{$code-copy-selector} .code-copy-button > .bi::before { + display: inline-block; + height: 1rem; + width: 1rem; + content: ""; + vertical-align: -0.125em; + @if variable-exists(btn-code-copy-color) { + background-image: url('data:image/svg+xml,'); + } @else { + background-image: url('data:image/svg+xml,'); + } + background-repeat: no-repeat; + background-size: 1rem 1rem; +} + +#{$code-copy-selector} .code-copy-button-checked > .bi::before { + @if variable-exists(btn-code-copy-color) { + background-image: url('data:image/svg+xml,'); + } @else { + background-image: url('data:image/svg+xml,'); + } +} + +@if variable-exists(btn-code-copy-color-active) { + #{$code-copy-selector} .code-copy-button:hover > .bi::before { + background-image: url('data:image/svg+xml,'); + } + #{$code-copy-selector} .code-copy-button-checked:hover > .bi::before { + background-image: url('data:image/svg+xml,'); + } +} + +$body-bg: #fff !default; +$tabset-border-color: rgb(222, 226, 230) !default; + +.panel-tabset [role="tablist"] { + border-bottom: 1px solid $tabset-border-color; + list-style: none; + margin: 0; + padding: 0; + width: 100%; +} + +.panel-tabset [role="tablist"] * { + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +@media (min-width: 30em) { + .panel-tabset [role="tablist"] li { + display: inline-block; + } +} + +.panel-tabset [role="tab"] { + border: 1px solid transparent; + border-top-color: $tabset-border-color; + display: block; + padding: 0.5em 1em; + text-decoration: none; +} + +@media (min-width: 30em) { + .panel-tabset [role="tab"] { + border-top-color: transparent; + display: inline-block; + margin-bottom: -1px; + } +} + +.panel-tabset [role="tab"][aria-selected="true"] { + background-color: $tabset-border-color; +} + +@media (min-width: 30em) { + .panel-tabset [role="tab"][aria-selected="true"] { + background-color: transparent; + border: 1px solid $tabset-border-color; + border-bottom-color: $body-bg; + } +} + +@media (min-width: 30em) { + .panel-tabset [role="tab"]:hover:not([aria-selected="true"]) { + border: 1px solid $tabset-border-color; + } +} + +.code-with-filename .code-with-filename-file { + margin-bottom: 0; + padding-bottom: 2px; + padding-top: 2px; + padding-left: 0.7em; + border: var(--quarto-border-width) solid var(--quarto-border-color); + border-radius: var(--quarto-border-radius); + border-bottom: 0; + border-bottom-left-radius: 0%; + border-bottom-right-radius: 0%; +} + +.code-with-filename div.sourceCode, +.reveal .code-with-filename div.sourceCode { + margin-top: 0; + border-top-left-radius: 0%; + border-top-right-radius: 0%; +} + +.code-with-filename .code-with-filename-file pre { + margin-bottom: 0; +} + +.code-with-filename .code-with-filename-file { + background-color: rgba(219, 219, 219, 0.8); +} + +.quarto-dark .code-with-filename .code-with-filename-file { + background-color: #555; +} + +.code-with-filename .code-with-filename-file strong { + font-weight: 400; +} + + +.reveal.center .slide aside, +.reveal.center .slide div.aside { + position: initial; +} + +section.has-light-background { + &, + h1, + h2, + h3, + h4, + h5, + h6 { + color: $light-bg-text-color; + } + a, + a:hover { + color: $light-bg-link-color; + } + code { + color: $light-bg-code-color; + } +} + +section.has-dark-background { + &, + h1, + h2, + h3, + h4, + h5, + h6 { + color: $dark-bg-text-color; + } + a, + a:hover { + color: $dark-bg-link-color; + } + code { + color: $dark-bg-code-color; + } +} + +#title-slide, +/* for when hash-type: number as identifier removed*/ +div.reveal div.slides section.quarto-title-block { + text-align: $presentation-title-slide-text-align; + .subtitle { + margin-bottom: 2.5rem; + } +} + +.reveal .slides { + text-align: $reveal-slide-text-align; +} + +.reveal .title-slide h1 { + font-size: $revealjs-h2-font-size; +} + +.reveal[data-navigation-mode="linear"] .title-slide h1 { + font-size: $revealjs-h1-font-size; +} + +.reveal div.sourceCode { + border: $border-width solid $code-block-border-color; + border-radius: $border-radius; +} + +.reveal pre { + width: 100%; + box-shadow: none; + background-color: $code-block-bg; + border: none; + margin: 0; + font-size: $revealjs-code-block-font-size; + line-height: $code-block-line-height; + font-family: $font-family-monospace-block; + + code { + background-color: $body-bg; // Insure we color output and code cell differently + font-size: inherit; + color: $code-block-color; + font-family: inherit; + } + + &.sourceCode code { + color: $code-block-color; + font-size: inherit; + background-color: inherit; + white-space: pre; + font-family: inherit; + padding: 6px 9px; + max-height: $code-block-height; + } +} + +// Inside code-file-name div, we want to use the same background color as decorated codeblock header +// https://github.com/quarto-dev/quarto-cli/issues/9560 +.reveal .code-with-filename .code-with-filename-file pre { + background-color: unset; +} + +.reveal code { + color: $code-color; + font-size: $revealjs-code-inline-font-size; + background-color: $code-bg; + white-space: pre-wrap; + font-family: $font-family-monospace-inline; +} + +.reveal .column-output-location { + display: flex; + align-items: stretch; +} + +.reveal .column-output-location .column:first-of-type div.sourceCode { + height: 100%; + background-color: $code-block-bg; +} + +.reveal blockquote { + display: block; + position: relative; + color: $border-color; + width: unset; + margin: var(--r-block-margin) auto; + padding: 0.625rem 1.75rem; + border-left: 0.25rem solid $text-muted; + font-style: normal; + background: none; + box-shadow: none; +} +.reveal blockquote p:first-child, +.reveal blockquote p:last-child { + display: block; +} + +.reveal .slide aside, +.reveal .slide div.aside { + position: absolute; + bottom: 20px; + font-size: #{$presentation-font-smaller}em; + color: $text-muted; +} + +.reveal .slide sup { + font-size: #{$presentation-font-smaller}em; +} + +.reveal .slide.scrollable aside, +.reveal .slide.scrollable div.aside { + position: relative; + margin-top: 1em; +} + +.reveal .slide aside .aside-footnotes { + margin-bottom: 0; +} + +.reveal .slide aside .aside-footnotes li:first-of-type { + margin-top: 0; +} + +$panel-sidebar-width: 270px; +$panel-sidebar-padding: 0.5em; + +.reveal .layout-sidebar { + display: flex; + width: 100%; + margin-top: 0.8em; +} + +.reveal .layout-sidebar .panel-sidebar { + width: $panel-sidebar-width; +} + +.reveal .layout-sidebar-left .panel-sidebar { + margin-right: calc(#{$panel-sidebar-padding} * 2); +} + +.reveal .layout-sidebar-right .panel-sidebar { + margin-left: calc(#{$panel-sidebar-padding} * 2); +} + +.reveal .layout-sidebar .panel-fill, +.reveal .layout-sidebar .panel-center, +.reveal .layout-sidebar .panel-tabset { + flex: 1; +} + +.reveal .panel-input, +.reveal .panel-sidebar { + font-size: 0.5em; + padding: $panel-sidebar-padding; + border-style: solid; + border-color: $input-panel-border-color; + border-width: $input-panel-border-width; + border-radius: $input-panel-border-radius; + background-color: $input-panel-bg; +} + +.reveal .panel-sidebar :first-child, +.reveal .panel-fill :first-child { + margin-top: 0; +} + +.reveal .panel-sidebar :last-child, +.reveal .panel-fill :last-child { + margin-bottom: 0; +} + +.panel-input > div, +.panel-input > div > div { + vertical-align: middle; + padding-right: 1em; +} + +.reveal p, +.reveal .slides section, +.reveal .slides section > section { + line-height: $revealjs-line-height; +} + +// Smaller font size logic +.reveal { + // When smaller is set globally + &.smaller { + .slides { + // We make the all slide font-size smaller by a factor of $presentation-font-smaller + section { + font-size: #{$presentation-font-smaller}em; + + // avoid applying twice the reduction when using nested section + section { + font-size: inherit; + } + } + + // But we don't want headers to change size and they are in em + h1 { + @include undo-smaller-font-size($revealjs-h1-font-size); + } + h2 { + @include undo-smaller-font-size($revealjs-h2-font-size); + } + h3 { + @include undo-smaller-font-size($revealjs-h3-font-size); + } + } + } + + .slides section { + // when smaller is set on slide + &.smaller { + font-size: #{$presentation-font-smaller}em; + + // But we don't want headers to change size and they are in em + h1 { + @include undo-smaller-font-size($revealjs-h1-font-size); + } + h2 { + @include undo-smaller-font-size($revealjs-h2-font-size); + } + h3 { + @include undo-smaller-font-size($revealjs-h3-font-size); + } + } + + // On callout we want to make the font-size smaller too + div.callout { + font-size: #{$presentation-font-smaller}em; + + // But we don't want headers to change size and they are in em + h1 { + @include undo-smaller-font-size($revealjs-h1-font-size); + } + h2 { + @include undo-smaller-font-size($revealjs-h2-font-size); + } + h3 { + @include undo-smaller-font-size($revealjs-h3-font-size); + } + } + } +} + +.reveal .columns > .column > :not(ul, ol) { + margin-left: 0.25rem; + margin-right: 0.25rem; +} + +.reveal .columns > .column:first-child > :not(ul, ol) { + margin-right: 0.5rem; + margin-left: 0; +} +.reveal .columns > .column:last-child > :not(ul, ol) { + margin-right: 0; + margin-left: 0.5rem; +} + +.reveal .slide-number { + color: $linkColorHover; + background-color: $body-bg; +} + +.reveal .footer { + color: $text-muted; + + a { + color: $linkColor; + } + + &.has-dark-background { + color: quarto-color.scale($dark-bg-text-color, $whiteness: 30%); + + a { + color: quarto-color.scale($dark-bg-link-color, $whiteness: 30%); + } + } + + &.has-light-background { + color: quarto-color.scale($light-bg-text-color, $whiteness: 30%); + + a { + color: quarto-color.scale($light-bg-link-color, $whiteness: 30%); + } + } +} + +.reveal .slide-number { + color: $text-muted; + + &.has-dark-background { + color: quarto-color.scale($dark-bg-text-color, $whiteness: 30%); + } + + &.has-light-background { + color: quarto-color.scale($light-bg-text-color, $whiteness: 30%); + } +} + +// handle caption for figures +.reveal .slide { + figure > figcaption, + img.stretch + p.caption, + img.r-stretch + p.caption { + font-size: #{$presentation-font-smaller}em; + } +} + +@media screen and (min-width: 500px) { + $arrow-spacing: 0.2em; + $control-arrow-spacing: 1.4em; + + .reveal .controls[data-controls-layout="edges"] .navigate-left { + left: $arrow-spacing; + } + + .reveal .controls[data-controls-layout="edges"] .navigate-right { + right: $arrow-spacing; + } + + .reveal .controls[data-controls-layout="edges"] .navigate-up { + top: $arrow-spacing * 2; + } + + .reveal .controls[data-controls-layout="edges"] .navigate-down { + bottom: $arrow-spacing - $control-arrow-spacing + 3.5em; + } +} + +.tippy-box[data-theme~="light-border"] { + background-color: $backgroundColor; + color: $mainColor; + border-radius: $border-radius; + border: solid $border-width $border-color; + font-size: 0.6em; +} + +.tippy-box[data-theme~="light-border"] .tippy-arrow { + color: $border-color; +} + +.tippy-box[data-placement^="bottom"] > .tippy-content { + padding: 7px 10px; + z-index: 1; +} + +.reveal .panel-tabset [role="tab"] { + padding: 0.25em 0.7em; +} + +.reveal .slide-menu-button .fa-bars::before { + background-image: url('data:image/svg+xml,'); +} + +.reveal .slide-chalkboard-buttons .fa-easel2::before { + background-image: url('data:image/svg+xml,'); +} + +.reveal .slide-chalkboard-buttons .fa-brush::before { + background-image: url('data:image/svg+xml,'); +} + +// This is a sentinel value that renderers can use to determine +// whether the theme is dark or light +@if ( + quarto-color.blackness($backgroundColor) > $code-block-theme-dark-threshhold +) { + /*! dark */ +} @else { + /*! light */ +} + +/* override theme.scss style for the default styles + + Note the need for both the case sensitive flag and the + case-oblivious selectors. This is a workaround for what's + apparently a Chrome and Safari bug: + + https://github.com/quarto-dev/quarto-cli/issues/1902#issuecomment-1219783059 + */ + +.reveal ol[type="a"] { + list-style-type: lower-alpha; +} + +.reveal ol[type="a" s] { + list-style-type: lower-alpha; +} + +.reveal ol[type="A" s] { + list-style-type: upper-alpha; +} + +.reveal ol[type="i"] { + list-style-type: lower-roman; +} + +.reveal ol[type="i" s] { + list-style-type: lower-roman; +} + +.reveal ol[type="I" s] { + list-style-type: upper-roman; +} + +.reveal ol[type="1"] { + list-style-type: decimal; +} + +// https://github.com/quarto-dev/quarto-cli/issues/2834 +// temporary workaround while we come up with a pure CSS solution + +.reveal ul.task-list { + list-style: none; +} +.reveal ul.task-list li input[type="checkbox"] { + width: 2em; + height: 2em; + margin: 0 1em 0.5em -1.6em; + vertical-align: middle; +} + +// https://github.com/quarto-dev/quarto-cli/issues/3380 + +div.cell-output-display div.pagedtable-wrapper table.table { + font-size: 0.6em; +} + +.reveal .code-annotation-container-hidden { + display: none; +} + +.reveal code.sourceCode button.code-annotation-anchor, +.reveal code.sourceCode .code-annotation-anchor { + font-family: $font-family-monospace; + color: var(--quarto-hl-co-color); + border: solid var(--quarto-hl-co-color) 1px; + border-radius: 50%; + font-size: 0.7em; + line-height: 1.2em; + margin-top: 2px; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + -o-user-select: none; +} + +.reveal code.sourceCode button.code-annotation-anchor { + cursor: pointer; +} + +.reveal code.sourceCode a.code-annotation-anchor { + text-align: center; + vertical-align: middle; + text-decoration: none; + cursor: default; + height: 1.2em; + width: 1.2em; +} + +.reveal code.sourceCode.fragment a.code-annotation-anchor { + left: auto; +} + +.reveal #code-annotation-line-highlight-gutter { + width: 100%; + border-top: solid var(--quarto-hl-co-color) 1px; + border-bottom: solid var(--quarto-hl-co-color) 1px; + z-index: 2; +} + +.reveal #code-annotation-line-highlight { + margin-left: -8em; + width: calc(100% + 4em); + border-top: solid var(--quarto-hl-co-color) 1px; + border-bottom: solid var(--quarto-hl-co-color) 1px; + z-index: 2; + margin-bottom: -2px; +} + +.reveal code.sourceCode .code-annotation-anchor.code-annotation-active { + background-color: var(--quarto-hl-normal-color, #aaaaaa); + border: solid var(--quarto-hl-normal-color, #aaaaaa) 1px; + color: rgb(red($code-block-bg), green($code-block-bg), blue($code-block-bg)); + font-weight: bolder; +} + +.reveal pre.code-annotation-code { + padding-top: 0; + padding-bottom: 0; + code { + z-index: 3; + padding-left: 0px; + } +} + +.reveal dl.code-annotation-container-grid { + margin-left: 0.1em; + dt { + margin-top: 0.65rem; + font-family: $font-family-monospace; + border: solid $body-color 1px; + border-radius: 50%; + height: 1.3em; + width: 1.3em; + line-height: 1.3em; + font-size: 0.5em; + text-align: center; + vertical-align: middle; + + text-decoration: none; + } + + dd { + margin-left: 0.25em; + } +} + +/* + + https://github.com/quarto-dev/quarto-cli/issues/4283 + + The trick itself is from here: + + https://stackoverflow.com/questions/8720931/can-css-detect-the-number-of-children-an-element-has + +*/ + +.reveal .scrollable ol li:first-child:nth-last-child(n + 10), +.reveal .scrollable ol li:first-child:nth-last-child(n + 10) ~ li { + margin-left: 1em; +} + +/* kbd rules */ + +kbd { + font-family: $font-family-monospace; + font-size: $kbd-font-size; + color: $kbd-color; + @include shift_to_dark( + "background-color", + shift-color($kbd-bg, 70%), + $kbd-bg + ); + border: 1px solid; + border-color: $code-block-border-color; + border-radius: 5px; + padding: $kbd-padding-y $kbd-padding-x; +} + +:root { + --r-inline-code-font: #{$font-family-monospace-inline}; + --r-block-code-font: #{$font-family-monospace-block}; + --r-inline-code-font-size: #{$revealjs-code-inline-font-size}; + --r-block-code-font-size: #{$revealjs-code-block-font-size}; +} + +// _brand.yml rules + +.reveal a { + font-weight: $link-weight; + background-color: $link-color-bg; + text-decoration: $link-decoration; +} + +/* Callout styles */ + +.reveal div.callout { + margin-top: $callout-margin-top; + margin-bottom: $callout-margin-bottom; + border-radius: $border-radius; + overflow-wrap: break-word; + + // Rules for both styles + &.callout-style-simple, + &.callout-style-default { + border-left: $callout-border-width solid #acacac; + border-right: solid 1px $table-border-color; + border-top: solid 1px $table-border-color; + border-bottom: solid 1px $table-border-color; + + div { + &.callout-body, + &.callout-title { + // Font size is inherited from the parent div.callout + // which is scaled down like .smaller + font-size: inherit; + border-bottom: none; + font-weight: 600; + } + + &.callout-title { + display: flex; + align-items: center; + + p { + margin-top: 0.5em; + margin-bottom: 0.5em; + color: var(--r-main-color); + } + } + } + + .callout-icon::before { + height: 1.25em; + width: 1.25em; + background-size: 1.25em 1.25em; + } + + &.callout-titled { + .callout-body { + > .callout-content { + > :last-child { + margin-bottom: var(--r-block-margin); + } + > :last-child:not(div.sourceCode) { + padding-bottom: 0.5rem; + margin-bottom: 0; + } + } + } + .callout-icon::before { + margin-top: 0.25em; + padding-right: 0.25em; + } + } + + &.no-icon::before { + display: none !important; + } + } + + // Apply only to simple callout style which could have + // - a title or not + // - an icon or not + &.callout-style-simple { + padding: 0em 0.5em; + display: flex; + + &.callout-titled { + .callout-body { + margin-top: 0.2em; + } + &:not(.no-icon) { + .callout-content { + padding-left: 1.6em; + } + } + .callout-content { + p { + margin-top: 0; + } + } + } + + &:not(.callout-titled) { + .callout-body { + display: flex; + } + .callout-icon::before { + margin-top: var(--r-block-margin); + padding-right: 0.5em; + } + .callout-body { + > .callout-content { + // Margin needs to be added when last child is div.sourceCode + // Other code cell border is mixed with callout border + > div.sourceCode:last-child { + margin-bottom: 1rem; + } + > :first-child { + margin-top: var(--r-block-margin); + } + } + } + } + + .callout-icon::before { + display: inline-block; + content: ""; + background-repeat: no-repeat; + } + + div { + &.callout-title { + opacity: 75%; + } + &.callout-body { + font-weight: 400; + } + } + } + + // Apply only to default callout style which could have + // - a title (can't have no title) + // - an icon or not + &.callout-style-default { + &.callout-titled { + .callout-content { + p { + margin-top: 0.7em; + } + } + } + + .callout-icon::before { + display: inline-block; + content: ""; + background-repeat: no-repeat; + } + + div { + &.callout-body { + font-weight: 400; + } + &.callout-title { + opacity: 85%; + padding-left: 0.5em; + padding-right: 0.5em; + } + &.callout-content { + padding-left: 0.5em; + padding-right: 0.5em; + } + } + } + + // FIXME: There is no body-container in revealjs so remove but before find what it was suppose to do + .callout-body-container { + flex-grow: 1; + } +} + +/* Callout Types */ + +// Generate per callout type css to customize their appearance +// Define the callouts for which we should define styles +$callouts: ( + // NOTE + "note": + ( + "color": $callout-color-note, + "icon": + '', + ), + // TIP + "tip": + ( + "color": $callout-color-tip, + "icon": + '', + ), + // WARNING + "warning": + ( + "color": $callout-color-warning, + "icon": + '', + ), + // CAUTION + "caution": + ( + "color": $callout-color-caution, + "icon": + '', + ), + // IMPORTANT + "important": + ( + "color": $callout-color-important, + "icon": + '', + ) +); + +@each $name, $info in $callouts { + $shifted-color: #{shift-color( + quarto-map.get($info, "color"), + $callout-icon-scale + )}; + $shifted-color-svg: str-replace($shifted-color, "#", "%23"); + + .reveal div.callout { + &.callout-#{$name} { + border-left-color: shift-color( + quarto-map.get($info, "color"), + $callout-border-scale + ); + &.callout-style-default { + .callout-title { + @include shift_to_dark( + "background-color", + shift-color(quarto-map.get($info, "color"), 70%), + shift-color(quarto-map.get($info, "color"), -90%) + ); + } + } + .callout-icon::before { + background-image: #{"url('data:image/svg+xml," + + str-replace( + quarto-map.get($info, "icon"), + 'fill="currentColor"', + 'style="fill: #{$shifted-color-svg}"' + ) + + "');"}; + } + } + } +} + + +// quarto-scss-analysis-annotation { "origin": "'rules' section from user-defined SCSS" } + +$presentation-author-block-padding-left: if( + $presentation-title-slide-text-align != left, + 0.5em, + 0 +); +$presentation-author-block-padding-right: if( + $presentation-title-slide-text-align != right, + 0.5em, + 0 +); + +.reveal { + .quarto-title-block { + .quarto-title-authors { + display: flex; + justify-content: $presentation-title-slide-text-align; + + .quarto-title-author { + padding-left: $presentation-author-block-padding-left; + padding-right: $presentation-author-block-padding-right; + + a, + a:hover, + a:visited, + a:active { + color: inherit; + text-decoration: none; + } + + .quarto-title-author-name { + margin-bottom: 0.1rem; + } + + .quarto-title-author-email { + margin-top: 0px; + margin-bottom: 0.4em; + font-size: 0.6em; + } + + .quarto-title-author-orcid { + img { + margin-bottom: 4px; + } + } + + .quarto-title-affiliation { + font-size: 0.7em; + margin-top: 0px; + margin-bottom: 8px; + } + + .quarto-title-affiliation:first { + margin-top: 12px; + } + } + } + } +} + + +.hide { + display: none !important; +} + +.slide-number, +.reveal.has-logo .slide-number { + bottom: 10px !important; + right: 100px !important; + top: unset !important; + color: $body-color !important; +} + +.slide-background-content { + background-size: contain !important; + background-repeat: no-repeat !important; + background-position: center !important; + opacity: 0.1 !important; +} + +.slide-background:first-child .slide-background-content { + opacity: 0.3 !important; +} + +.reveal blockquote { + border-left-color: $primary !important; + color: $primary !important; +} + +.reveal a { + text-decoration: underline; +} + +.smallest { + font-size: 1rem; +} + +.max-lines { + max-height: 400px; /* Adjust height as needed */ + overflow: scroll; /* Enable scrolling if content exceeds height */ +} + +.scrolling { + overflow: scroll; + height: 400px; +} + +.reveal ol { + margin: 0 0 0 1.5em; +} + +.reveal code { + background-color: #F1F3F5; + color: blue; + padding: 0.1em 0.2em; + border-radius: 3px; +} + +.reveal .slide-logo { + height: 1.75em !important; + width: 1.75em !important; + max-width: unset !important; + max-height: unset !important; +} + +.reveal div.sourceCode { + border: 1px solid #808080 !important; + border-radius: 6px !important; + box-shadow: 4px 4px 12px #b0b0b0 !important; +} + +.reveal pre { + /* Remove the inner border */ + border: none !important; + box-shadow: none !important; +} + +.reveal pre code { + padding: 10px !important; + border: none !important; + box-shadow: none !important; +} + +code { + padding: 5px !important; + color: black !important; + background-color: #fcf3da !important; + border: 1px solid #808080; + border-radius: 6px !important; +} + +.smallest code { + padding: 1px !important; +} + +.coloring { + color: black !important; + background-color; #E1EAE2; +} + +// quarto-scss-analysis-annotation { "origin": null } \ No newline at end of file diff --git a/inst/slides/img/final_table.png b/inst/slides/img/final_table.png index 976238f..24c9373 100644 Binary files a/inst/slides/img/final_table.png and b/inst/slides/img/final_table.png differ diff --git a/inst/slides/img/table-with-goodies-1.png b/inst/slides/img/table-with-goodies-1.png new file mode 100644 index 0000000..6405654 Binary files /dev/null and b/inst/slides/img/table-with-goodies-1.png differ diff --git a/inst/slides/img/table-with-goodies-2.png b/inst/slides/img/table-with-goodies-2.png new file mode 100644 index 0000000..6937306 Binary files /dev/null and b/inst/slides/img/table-with-goodies-2.png differ diff --git a/inst/slides/img/table-with-goodies-3.png b/inst/slides/img/table-with-goodies-3.png new file mode 100644 index 0000000..c2be140 Binary files /dev/null and b/inst/slides/img/table-with-goodies-3.png differ diff --git a/inst/slides/img/table-with-goodies-4.png b/inst/slides/img/table-with-goodies-4.png new file mode 100644 index 0000000..1cc7b34 Binary files /dev/null and b/inst/slides/img/table-with-goodies-4.png differ diff --git a/inst/slides/img/table-with-goodies-5.png b/inst/slides/img/table-with-goodies-5.png new file mode 100644 index 0000000..5794a7b Binary files /dev/null and b/inst/slides/img/table-with-goodies-5.png differ diff --git a/inst/slides/img/table-with-goodies-6.png b/inst/slides/img/table-with-goodies-6.png new file mode 100644 index 0000000..7838b36 Binary files /dev/null and b/inst/slides/img/table-with-goodies-6.png differ diff --git a/inst/slides/index.qmd b/inst/slides/index.qmd index e2b40f3..4975542 100644 --- a/inst/slides/index.qmd +++ b/inst/slides/index.qmd @@ -28,317 +28,289 @@ options( > **sumExtras**: "gt**sum**mary table **Extras**" -## The plan +## What we'll learn: + ::: {.incremental} -+ Apply a JAMA-ready theme -+ Customize missing values with `clean_table()` -+ One-line table formatting with `extras()` -+ Auto-label variables with `add_auto_labels()` - + Create a variable dictionary - + Set package options -+ Add variable group headers -+ Style and color group rows -+ Put it all together in one pipeline ++ where are the slides? ๐Ÿ˜ ++ `gtsummary` basics ++ Table theming ++ Variable labeling ++ Address missing values ++ The `extras()` function ++ Variable grouping & styling ::: -## What we'll build +## Go from this... + +```{r} +#| echo: false +trial |> + tbl_summary(by = trt) +``` + + +## To this! ![](img/final_table.png){fig-align="center"} -## Creating a summary table +## And the slides are here: + +https://slides.kyleGrealis.com/sumExtras -Results from a simulated study of two chemotherapy agents. +The docs: +https://www.kyleGrealis.com/sumExtras -A dataset containing the baseline characteristics of 200 patients who received Drug A or Drug B. Dataset also contains the outcome of tumor response to the treatment. +How to install: ```{r} -#| echo: true -#| eval: true -gtsummary::trial +#| eval: false +install.packages("sumExtras") +pak::pak("sumExtras") +pak::pak("kyleGrealis/sumExtras") + +# Watch me break things: +remotes::install_github("kyleGrealis/sumExtras@dev") ``` -## -Most times, we're faced with datasets with missing values. -Let's `mutate` in some `0` and `NA` values. +## `gtsummary::trial` dataset + +Results from a simulated study of two chemotherapy agents + +A dataset containing the baseline characteristics of 200 patients who received +Drug A or Drug B. Dataset also contains the outcome of tumor response to the +treatment. ```{r} #| echo: true #| eval: true -#| code-line-numbers: "|17" -dat <- trial |> - mutate( - marker = case_when( - trt == "Drug B" ~ 0, - .default = marker - ), - grade = case_when( - trt == "Drug B" ~ NA, - .default = grade - ) - ) -head(dat, 5) +gtsummary::trial ``` -## Initial `gtsummary` table -:::: {.columns} -::: {.column} -```{r} -#| eval: false -dat |> - tbl_summary(by = trt) -``` -::: {.fragment} -Oh no! This table seems to run off the page! -::: +## `gtsummary` basics (part 1) -::: +Create a stratified table using the `by` parameter -::: {.column} ```{r} -#| echo: false -dat |> - tbl_summary(by = trt) +#| output-location: column +trial |> + gtsummary::tbl_summary(by = trt) ``` -::: -:::: - -## Setting a theme -Use `sumExtras::use_jama_theme()` for a more compact table. +## `gtsummary` basics (part 2) -::: {.fragment} -`use_jama_theme()` is just a wrapper function for `gtsummary::theme_gtsummary_compact("jama")` -::: +Setting a compact theme -
- -:::: {.columns} -::: {.column} ```{r} -#| eval: false +#| output-location: column +#| code-line-numbers: "|2" +# Set once at the top of your table script sumExtras::use_jama_theme() -dat |> +trial |> tbl_summary(by = trt) ``` + +::: {.incremental .smaller} +* `use_jama_theme()` is a wrapper function for \ + `gtsummary::set_gtsummary_theme(theme_gtsummary_compact("jama"))` +* If your data contains label attributes, `gtsummary` will print +those automatically, +as in the table above ::: -::: {.column} + +## `gtsummary` basics (part 3) + +Pass a list of labels + ```{r} -#| echo: false -use_jama_theme() -dat |> - tbl_summary(by = trt) +#| output-location: column +#| code-line-numbers: "|4-12" +trial |> + tbl_summary( + by = trt, + label = list( + age ~ "Age (years)", + marker ~ "Marker Level (ng/mL)", + stage ~ "T Stage", + grade ~ "Tumor Grade", + response ~ "Tumor Response", + death ~ "Patient Died", + ttdeath ~ "Month to Death/Censor" + ) + ) ``` -::: -:::: +## Add labels, *p*-values, and other goodies +![](img/table-with-goodies-1.png){width=85%} -## Cleaning the table +This is more like what I want... -![](img/tbl_with_zeros.png){width="50%"} +## Add labels, *p*-values, and other goodies +![](img/table-with-goodies-2.png){width=85%} -Draw attention to complete, non-`NA` values with `clean_table()` by replacihng `0 (0.00, 0.00)`, `0 (NA%)`, and other similar values with an emdash (`โ€”`). +Removed "Characteristic" from variable heading -## Cleaning the table +## Add labels, *p*-values, and other goodies +![](img/table-with-goodies-3.png){width=85%} -```{r} -#| eval: false -dat |> - tbl_summary(by = trt) |> - clean_table() -``` +Added *p*-values -:::: {.columns} -::: {.column} -Without `clean_table()` -```{r} -#| echo: false -use_jama_theme() -dat |> - tbl_summary(by = trt) -``` -::: +## Add labels, *p*-values, and other goodies +![](img/table-with-goodies-4.png){width=85%} + +Bold labels & significant *p*-values + +## Add labels, *p*-values, and other goodies +![](img/table-with-goodies-5.png){width=85%} + +Added "Overall" column + +## Add labels, *p*-values, and other goodies +![](img/table-with-goodies-6.png){width=85%} + +Clean up missing values + + +## Simulate missing values -::: {.column} -With `clean_table()` ```{r} -#| echo: false -use_jama_theme() -dat |> - tbl_summary(by = trt) |> - clean_table() +#| echo: true +#| code-line-numbers: "|4,8,18,22" +trial <- trial |> + mutate( + marker = case_when( + trt == "Drug B" ~ 0, + .default = marker + ), + grade = case_when( + trt == "Drug B" ~ NA, + .default = grade + ) + ) + +head(trial) ``` -::: -:::: -## Adding labels, *p*-values, and other goodies + +## `gtsummary` basics (part 4) + +Add *p*-values, bold labels, and introducing `clean_table()` ```{r} -#| echo: false -dat |> +#| output-location: column +#| code-line-numbers: "|14|15|16|17|18|19" +trial |> tbl_summary( by = trt, label = list( - age ~ "Age at Enrollment (years)", + age ~ "Age (years)", marker ~ "Marker Level (ng/mL)", stage ~ "T Stage", grade ~ "Tumor Grade", response ~ "Tumor Response", - death ~ "Patient Died" + death ~ "Patient Died", + ttdeath ~ "Month to Death/Censor" ) - ) |> + ) |> modify_header(label = "") |> bold_labels() |> add_p() |> bold_p() |> - add_overall() |> - clean_table() + add_overall() |> + sumExtras::clean_table() ``` -::: {.incremental .smallest} -* Removed "Characteristic" from variable heading -* Added *p*-values -* Bold labels & significant *p*-values -* Added "Overall" column -* Applied `clean_table()` + + +## `clean_table()` + +![](img/tbl_with_zeros.png){width="45%"} + +**THE GOAL:** + ++ Draw attention to complete data. + +::: {.fragment} ++ Replace missingness patterns with an emdash (`โ€”`). ::: +## Cleaning the table -## Adding labels, *p*-values, and other goodies +```{r} +#| eval: false +trial |> + tbl_summary(by = trt) |> + clean_table() +``` :::: {.columns} ::: {.column} +Without `clean_table()` ```{r} -#| eval: false -dat |> +#| echo: false +trial |> tbl_summary( by = trt, label = list( - age ~ "Age at Enrollment (years)", + age ~ "Age (years)", marker ~ "Marker Level (ng/mL)", stage ~ "T Stage", grade ~ "Tumor Grade", response ~ "Tumor Response", - death ~ "Patient Died" + death ~ "Patient Died", + ttdeath ~ "Month to Death/Censor" ) - ) |> - modify_header(label = "") |> - bold_labels() |> - add_p() |> - bold_p() |> - add_overall() |> - clean_table() + ) ``` ::: ::: {.column} +With `clean_table()` ```{r} -#| eval: false -dat |> +#| echo: false +trial |> tbl_summary( by = trt, label = list( - age ~ "Age at Enrollment (years)", + age ~ "Age (years)", marker ~ "Marker Level (ng/mL)", stage ~ "T Stage", grade ~ "Tumor Grade", response ~ "Tumor Response", - death ~ "Patient Died" + death ~ "Patient Died", + ttdeath ~ "Month to Death/Censor" ) - ) |> - extras() + ) |> + clean_table() ``` -:::{.fragment} -Six lines converted to one! -::: - ::: - :::: -# Adding labels - -## Default table without labels - -`gtsummary` reads attribute labels natively! - -:::: {.columns} -::: {.column width="45%"} -```{r} -#| eval: false -# Set an explicit attribute label: -attr(dat$age, "label") <- - "Age FROM ATTRIBUTE (years)" - -dat |> - tbl_summary(by = trt) |> - extras() -``` - -::: {.fragment .smaller} -`dat` was created from `trial`. `trial` had attributes, but using `mutate` -earlier removed attributes from `dat$marker` and `dat$grade`. -::: - -::: +# Reusable labeling -::: {.column width="55%"} - -```{r} -#| echo: false -attr(dat$age, "label") <- "Age FROM ATTRIBUTE (years)" -dat |> - tbl_summary(by = trt) |> - extras() -``` +::: {.incremental} +* Create a variable `dictionary` object +* Use `sumExtras` auto-labeling ::: -:::: - - -## The `gtsummary::tbl_*` labeling method - -Declare the variables labels within the `tbl_*` function: - -```{r} -#| eval: false -dat |> - tbl_summary( - by = trt, - label = list( - age ~ "Age at Enrollment (years)", - marker ~ "Marker Level (ng/mL)", - stage ~ "T Stage", - grade ~ "Tumor Grade", - response ~ "Tumor Response", - death ~ "Patient Died" - ) - ) |> - extras() -``` -## Creating a Variable Dictionary +## Creating a variable dictionary -A dictionary object is a data frame with two columns: -* `variable` -* `description` - - +A `dictionary` is a data frame with `variable` and `description` columns. :::: {.columns} ::: {.column width="55%"} ```{r} #| echo: true -#| code-line-numbers: "|1|2" +#| code-line-numbers: "|2" dictionary <- tibble::tribble( ~variable, ~description, "trt", "Chemotherapy Treatment", @@ -351,179 +323,155 @@ dictionary <- tibble::tribble( ) ``` ::: -::: {.column width="45%" .fragment} +::: {.column width="45%"} ```{r} dictionary ``` ::: :::: -::: {.incremental} -::: {.callout-note .fragment} -The column names are case-*in*sensitive. `variable`, `Variable`, and `VARIABLE` -will work all the same. -::: +::: {.fragment} +With a `dictionary` in scope, labels can be applied automatically. ::: -## Use the `dictionary` with `add_auto_labels()` - -The `dictionary` is found by `sumExtras` but manual labelling inside the -`tbl_*` function **always** wins! - -:::: {.columns} -::: {.column width="45%"} -```{r} -#| eval: false -dat |> - tbl_summary( - by = trt, - label = list( - age ~ "Age FROM LABEL (years)", - marker ~ "Marker Level LABEL (ng/mL)", - stage ~ "T Stage LABEL", - grade ~ "Tumor Grade LABEL", - response ~ "Tumor Response LABEL", - death ~ "Patient Died LABEL" - ) - ) |> - extras() |> - add_auto_labels() -``` -::: - -::: {.column width="55%"} +## Using `add_auto_labels()` ```{r} -#| echo: false -dat |> - tbl_summary( - by = trt, - label = list( - age ~ "Age FROM LABEL (years)", - marker ~ "Marker Level LABEL (ng/mL)", - stage ~ "T Stage LABEL", - grade ~ "Tumor Grade LABEL", - response ~ "Tumor Response LABEL", - death ~ "Patient Died LABEL" - ) - ) |> - extras() |> - add_auto_labels() -``` -::: -:::: - - -## Use the `dictionary` with `add_auto_labels()` (part 2) - -Remove the manual labels and use the `dictionary`. +#| output-location: column +#| code-line-numbers: "|1-3|8-16|24" +options( + sumExtras.prefer_dictionary = TRUE +) -:::: {.columns} -::: {.column width="45%"} -```{r} -#| eval: false -dat |> +trial |> tbl_summary( by = trt, # label = list( - # age ~ "Age FROM LABEL (years)", + # age ~ "Age (years)", # marker ~ "Marker Level (ng/mL)", # stage ~ "T Stage", # grade ~ "Tumor Grade", # response ~ "Tumor Response", - # death ~ "Patient Died" + # death ~ "Patient Died", + # ttdeath ~ "Month to Death/Censor" # ) - ) |> - extras() |> - add_auto_labels( - dictionary = dictionary - ) + ) |> + modify_header(label = "") |> + bold_labels() |> + add_p() |> + bold_p() |> + add_overall() |> + clean_table() |> + add_auto_labels() ``` -::: {.fragment} -UH OH! Where are my `dictionary` labels?? -::: -::: +## Reduce the code -::: {.column width="55%"} +No more writing `label = list(...)` for every table! ```{r} -#| echo: false -dat |> - tbl_summary( - by = trt, - label = list( - # age ~ "Age FROM LABEL (years)", - marker ~ "Marker Level (ng/mL)", - stage ~ "T Stage", - grade ~ "Tumor Grade", - response ~ "Tumor Response", - death ~ "Patient Died" - ) - ) |> - extras() |> - add_auto_labels(dictionary = dictionary) +#| output-location: column +options( + sumExtras.prefer_dictionary = TRUE +) + +trial |> + tbl_summary(by = trt) |> + modify_header(label = "") |> + bold_labels() |> + add_p() |> + bold_p() |> + add_overall() |> + clean_table() |> + add_auto_labels() ``` -::: -:::: -## Use the `dictionary` with `add_auto_labels()` (part 3) +## Reduce the code some more: -
-Set session (or persistent options): +Introducing `extras()` ```{r} -#| echo: true -#| eval: true +#| output-location: column +#| code-line-numbers: "|2|8-14|2,15" options( sumExtras.auto_labels = TRUE, sumExtras.prefer_dictionary = TRUE ) + +trial |> + tbl_summary(by = trt) |> + # modify_header(label = "") |> + # bold_labels() |> + # add_p() |> + # bold_p() |> + # add_overall() |> + # clean_table() |> + # add_auto_labels() + extras() ``` -::: {.incremental} -* No longer need to use the `add_auto_labels()` function - * `extras()` will now add the labels! -* The `dictionary` object will be found & used -::: -## Using `dictionary` and `options()` +## Code comparison :::: {.columns} -::: {.column width="45%"} +::: {.column} ```{r} #| eval: false -dat |> - tbl_summary(by = trt) |> - extras() +#| code-line-numbers: "4-12,14-20" +trial |> + tbl_summary( + by = trt, + label = list( + age ~ "Age (years)", + marker ~ "Marker Level (ng/mL)", + stage ~ "T Stage", + grade ~ "Tumor Grade", + response ~ "Tumor Response", + death ~ "Patient Died", + ttdeath ~ "Month to Death/Censor" + ) + ) |> + modify_header(label = "") |> + bold_labels() |> + add_p() |> + bold_p() |> + add_overall() |> + clean_table() |> + add_auto_labels() ``` - ::: -::: {.column width="55%"} - +::: {.column} ```{r} -#| echo: false -dat |> - tbl_summary(by = trt) |> +#| eval: false +#| code-line-numbers: "3" +trial |> + tbl_summary(by = trt) |> extras() ``` + +Sixteen lines converted to one! + ::: + :::: +:::{.fragment} +Not including the `options()` and `dictionary` ๐Ÿ˜‰ +::: -# Grouping variables & adding color -## Group variables +# Variable grouping & adding color + +## Variable grouping {.col-45-55} -:::: {.columns} -::: {.column width="45%"} ```{r} -#| eval: false -dat |> +#| output-location: column +#| code-line-numbers: "|12" +trial |> tbl_summary(by = trt) |> extras() |> add_variable_group_header( @@ -534,20 +482,21 @@ dat |> header = "Clinical Measures", variables = grade:ttdeath ) |> - add_group_styling() + sumExtras::add_group_styling() ``` -::: {.fragment} -`add_group_styling()` uses **bold** & *italics* by default and restores -left-justified variable indentation. -::: +::: {.incremental} ++ **bold** & *italic* group headers ++ restores left-justified variable indentation ::: -::: {.column width="55%"} + +## Add gray group background {.col-45-55} ```{r} -#| echo: false -dat |> +#| output-location: column +#| code-line-numbers: "|13" +trial |> tbl_summary(by = trt) |> extras() |> add_variable_group_header( @@ -558,19 +507,17 @@ dat |> header = "Clinical Measures", variables = grade:ttdeath ) |> - add_group_styling() + sumExtras::add_group_styling() |> + sumExtras::add_group_colors() ``` -::: -:::: -## Add grouping colors +## Add color group background {.col-45-55} -:::: {.columns} -::: {.column width="45%"} ```{r} -#| eval: false -dat |> +#| output-location: column +#| code-line-numbers: "|13-15" +trial |> tbl_summary(by = trt) |> extras() |> add_variable_group_header( @@ -581,9 +528,9 @@ dat |> header = "Clinical Measures", variables = grade:ttdeath ) |> - add_group_styling() |> - add_group_colors( - color = c("#E3F2FD", "#FFF9E6") + sumExtras::add_group_styling() |> + sumExtras::add_group_colors( + color = c("#E1EAE2", "#FFF9E6") ) ``` @@ -591,103 +538,127 @@ dat |> **NOTE:** `add_group_colors()` CONVERTS the table to a `gt` table to apply the tweaks! Use this function as the *last* step in the pipeline. ::: -::: -::: {.column width="55%"} +## And this is what we built! + +![](img/final_table.png){fig-align="center"} + + +# Extra `extras()`!!! + +What if you don't want the defaults? + +## `extras()` parameters ```{r} -#| echo: false -dat |> - tbl_summary(by = trt) |> - extras() |> - add_variable_group_header( - header = "Demographics", - variables = age:stage - ) |> - add_variable_group_header( - header = "Clinical Measures", - variables = grade:ttdeath - ) |> - add_group_styling() |> - add_group_colors(color = c("#E3F2FD", "#FFF9E6")) +#| eval: false +#| code-line-numbers: "|2|3|4|5|6|7-8" +extras( + pval = TRUE, # or FALSE + overall = TRUE, # or FALSE + last = FALSE, # or TRUE; gtsummary::add_overall() default + header = "", # removes "Characteristic" header + symbol = "โ€”", # display value for clean_table() + .args = NULL, # list override the default (next slide) + .add_p_args = NULL # gtsummary::add_p() arguments +) +``` + +## Overrides + +```{r} +#| eval: false +#| code-line-numbers: "|1-2|4-5|7-8|10-11|13-14" +# Remove p-values +extras(pval = FALSE) + +# Remove Overall column +extras(overall = FALSE) + +# Move Overall column to right-most column +extras(last = TRUE) + +# Change "Characteristic" header +extras(header = "Variables") + +# Use an emoji for clean_table() +extras(symbol = "๐Ÿคจ") +``` + + +## `extras()` using `.args` parameter + +Create reusable `extras()` arguments to override defaults: + +```{r} +#| output-location: column +#| code-line-numbers: "|1-5|1-5,9" +extra_args <- list( + pval = FALSE, + last = TRUE, + symbol = "๐Ÿคจ" +) + +trial |> + tbl_summary(by = trt) |> + extras(.args = extra_args) +``` + + +## `extras()` using `.add_p_args` + +Create custom *p*-value testing & styling + +```{r} +#| eval: false +#| code-line-numbers: "|1-5|10" +p_args <- list( + test = list(age ~ "t.test", marker ~ "t.test"), + pvalue_fun = ~ gtsummary::style_pvalue( + .x, digits = 2 + ) +) + +trial |> + tbl_summary(by = trt) |> + extras(.add_p_args = p_args) ``` -::: -:::: -## Reduced code! +## Match gtsummary tables to gt tables :::: {.columns} + ::: {.column} + ```{r} -#| eval: false -dat |> +trial |> tbl_summary( by = trt, - label = list( - age ~ "Age (years)", - marker ~ "Marker Level (ng/mL)", - stage ~ "T Stage", - grade ~ "Tumor Grade", - response ~ "Tumor Response", - death ~ "Patient Died" - ) - ) |> - modify_header(label = "") |> - bold_labels() |> - add_p() |> - bold_p() |> - add_overall() |> - clean_table() |> - add_variable_group_header( - header = "Demographics", - variables = age:stage + include = c(age, grade, marker) ) |> - add_variable_group_header( - header = "Clinical Measures", - variables = grade:ttdeath - ) |> - add_group_styling() |> - add_group_colors( - color = c("#E3F2FD", "#FFF9E6") - ) + extras() ``` ::: + ::: {.column} ```{r} -#| eval: false -dat |> - tbl_summary(by = trt) |> - extras() |> - add_variable_group_header( - header = "Demographics", - variables = age:stage - ) |> - add_variable_group_header( - header = "Clinical Measures", - variables = grade:ttdeath - ) |> - add_group_styling() |> - add_group_colors( - color = c("#E3F2FD", "#FFF9E6") - ) +#| code-line-numbers: "|5" +trial |> + select(trt, age, grade, marker) |> + head(10) |> + gt() |> + theme_gt_compact() ``` -::: {.fragment} -We just cut our code **in half**... -and that was only one table! -::: - ::: :::: -# Almost there... - -## `sumExtras` supports most `gtsummary` table types +## `sumExtras` supports most `gtsummary` tables with either partial or full features available. @@ -697,7 +668,7 @@ with either partial or full features available. * `tbl_uvregression()` * `tbl_strata()` * `tbl_merge()` -* `tbl_cross()` + ## THANK YOU! ๐Ÿ˜„ @@ -706,7 +677,7 @@ with either partial or full features available. **GitHub**: https://github.com/kyleGrealis -**Email**: kyleGrealis@proton.me +**Email**: kyle@kyleGrealis.com **Slides**: https://slides.kyleGrealis.com/sumExtras @@ -715,37 +686,3 @@ with either partial or full features available. ![](img/kyle.jpg){.styled-image .absolute bottom="30%" right="1%" height="auto" width="190"} - - diff --git a/inst/slides/slides.scss b/inst/slides/slides.scss index 4761f71..8c105a8 100644 --- a/inst/slides/slides.scss +++ b/inst/slides/slides.scss @@ -46,6 +46,10 @@ $font-family-sans-serif: "Google Sans", sans-serif; text-decoration: underline; } +.smaller { + font-size: 1.5rem; +} + .smallest { font-size: 1rem; } @@ -103,3 +107,19 @@ code { border: 1px solid #808080; border-radius: 6px !important; } + +.smallest code { + padding: 1px !important; +} + +.coloring { + color: black !important; + background-color: lightgray; +} + +.reveal .col-45-55 div.column:first-child { + width: 45%; +} +.reveal .col-45-55 div.column:not(:first-child) { + width: 55%; +} \ No newline at end of file diff --git a/inst/transcript.qmd b/inst/transcript.qmd new file mode 100644 index 0000000..7fcf9a8 --- /dev/null +++ b/inst/transcript.qmd @@ -0,0 +1,288 @@ + +## Title +โ€ŠGood afternoon, RMed. My name is Kyle Grealis, and today I will show +how easy it is to use the sumExtras package for streamlining table +workflows in clinical research + +## Intro +sumExtras is a R CRAN package created to enhance my workflows with gtsummary. +Now, I *love* gtsummary and it has been my go-to table-making package for a while now. +The output tables are elegant, clean and readable. +But oftentimes i would rewrite the same bits of code over and over. +And when you do that, we're advised it's best to write a function -- don't repeat yourself, right? +So I wrote a few wrapper functions. And then I was writing the same wrapper functions in multiple projects. +Ah yes, it was time to convert these repeatable lines of code to a package! +Simply put: sumExtras is a group of extra functions for using gtsummary tables. + +## The plan [incremental] +Today we will learn: +* how to apply a JAMA-ready table style theme +* customize missing values using the clean table function +* learn the power of the extras function +* quickly apply automatic variable labeling +* group like-variables and [CLICK] apply font styling & background colors to those group headers +* We will do this while progressively building our code pipeline. + +## What we'll build +The final table we build today will look like this, and I will demonstrate how to +reduce our lines of code while adding custom table styling bits throughout. +Let's offer bespoke tables to our colleagues and fellow stakeholders! + +## Creating a summary table +The gtsummary package provides the trial dataset, complete with synthetic information +of 200 patients who received one of two treatment drugs. Notice the data is... +not what we normally receive! +Here we have complete records, but let's make this more real-world in order to +visualize other features available with sumExtras. + +## +We'll begin by mutating our data, replacing the values for marker and grade [CLICK] +with zeroes and NA values when the treatment drug is B. +We'll save this as a new dataset, "dat". [CLICK] +Notice on line 17 the 0 and NA values for marker & grade, respectively. + +## Initial gtsummary table +Our initial gtsummary table is packed with great output. +It's beautiful and has a clean font making it easily readable. +The statistics are nicely grouped and this immediately is a tremendous improvement +over shipping generic console output to your PIs. +We can begin to improve this table by + +## Setting a theme +...setting a theme. I've often reached for the JAMA theme because it takes the +initial table and makes it nice and compact. +sumExtras provides the use_jama_theme function you can set once at the top of your +code to use for every table. +[CLICK] +You can see that this is nothing more than reducing how much code I needed to +remember... it's a wrapper around an existing gtsummary function. + + +## Cleaning the table +So we made a nicely-themed, more compact table. +However, we have zeros and missing values clogging up real estate. +[CLICK] +Let's set a goal to replace the all-zero and all-NA filled cells. +Perhaps we can remove some noise and direct the attention to the non-missingness +of our data. + +## Cleaning the table +When we pipe our table code to clean_table, we replace the cells that have +mean & standard deviation values of all-zero or counts cells of zero with NA percentage +with a cleaner emdash. +This is a default replacement placeholder that you can adjust... you can even use an emoji if you'd like. +If you compare the left & right output for Drug B, I find that my attention is now drawn to what truly matters. +This effect is more noticeable with more amount of missingness. + +## Adding labels... +This output is closer to our final table and more of what I want. +[CLICK] +The header "Characteristic" has been removed +[CLICK] +I've added a p-value column on the right of the table +[CLICK] +The variable labels and significant p-values are now bolded +[CLICK] +An Overall column has been added to the first output column position +[CLICK] +And lastly the clean table function has replace the noise of missingness +as we've just seen with clean table. + +[quick pause] +[CLICK] +The same table was created with these code cells +all while replacing the bottom six lines of code on the left +[CLICK] +with one pipe to extras on the right! +Another feature of sumExtras is how we can reduce redundancies with labeling. + +## Labeling hierarchy +Using gtsummary and sumExtras requires a small bit of understanding labeling hierarchy +[CLICK] +First, using gtsummary's label parameter ALWAYS wins +[CLICK] +Next, if our data contains variable label attributes, those are second in order +[CLICK] +Finally, sumExtras auto labeling is last. + +## Default table without labels +Now, the trial dataset contains variable attributes. +However, let's explicitly add the word "ATTRIBUTE" to the age variable. +This will become more apparent as we continue. +Now, notice in the output on the right, the age has that big attribute. +[CLICK] +The variables marker and grade do not have any nice styled labels, +and that's because when we initially mutated our data on slide 6, label attributes +were stripped from marker and grade. + +## The gtsummary::tbl_* labeling method +Labels set using the label parameter will always override attributes as +they are natively greater in the labeling hierarchy. +This action is native to gtsummary. +[CLICK] +Let's now also set the attribute for marker +[CLICK] +and also add the label parameter label for marker. +This will highlight the labeling hierarchy concept because notice +the output table. The variable label for marker is "marker label". +[CLICK] +Also, notice the variable grade still has no label since it does not have +a label attribute from the previous mutate nor was any label assigned in the +tbl_summary function. + +## Creating a variable dictionary +We have one final labeling method at our disposal. +Let's create a dictionary object and add a very-visual "DICT" to our variable labels. +[CLICK] +The dictionary requires the variable and description columns. +[CLICK] +The dictionary object can exist with any case-insensitive version of the words +variable and description: lowercase, title case, or uppercase. +This can also be an object you import from Excel. + +## Use the dictionary with add_auto_labels +We will continue with our explicit examples and add parameter labels within +the tbl_summary function. +I've added "label" here to all the variable labels to stress the understanding +of the labeling hierarchy. +[CLICK] +We then pipe this to add_auto_labels, but nothing changed in our table output. +There aren't any labels with the explicit DICT from the dictionary. +Why? Again, the labels set in the label parameter always take priority. + +## Use the dictionary with add_auto_labels part 2 +But we actually want the auto-labeling! +[CLICK] +We'll begin by commenting out many of the label parameter arguments +so add auto labels can work as intended. +Notice the output table now has "age from label" and +the DICT for grade originating from the dictionary object. +[pause] +If we look closely, marker has the attribute label that was set on slides 20. +T stage and the last 3 variables retained the label attributes from the +trial dataset that our dat dataset originated from. +We now have all three methods shown on one table: explicit labeling, +attributes, then sumExtras dictionary objects. + +## Use the dictionary with add_auto_labels part 3 +sumExtras offers some session options you can set at the beginning of your +table script or choose to include these in your Rprofile. +This will reset the hierarchy. +[CLICK] +The dictionary now takes precedence as the preferred or primary labeling method. +[CLICK] +And you no longer need to call the add auto labels function ONLY IF +[CLICK] +you use the extras function in the pipe. extras will find the dictionary +in your session environment and apply the labels automatically. + +## Applying the dictionary +Kinda like magic, right? :) +First we set the options before we create the tables +Then just pipe to extras! +You'll now notice that the output on the right has the new labeling hierarchy +with DICT being displayed and nothing specifying attribute for age or marker. +I promise this will be clearer to you as you use it. +Or reach out and I will gladly give a hand. + +## Grouping variables & adding color +Let's add grouping for like variables. +The trial data has demographic and clinical measures + +## Group variables +We will use the gtsummary add variable group header and then pipe that to +[CLICK] +add group styling. +And what you get right outta the box with this is +[CLICK] +bold and italicized grouping labels and it restores left justified variable indentation. +What does that mean?! +Well, using add_variable group header without sumExtras' add group styling, +variable labels are indented 2 places and are vertically aligned with +the factor levels. +Add group styling removes that indent and restores left-justification. +A minor tweak but, to me, adds value... + +## Add grouping colors +Especially when we add group colors. +[CLICK] +Since we have 2 variable groupings, we can set a color for each group header. +I chose to use each of the colors for the sumExtras logo. +The default color is a submission-worthy light gray, if you chose to omit the +color parameter & argument. +[CLICK] +Just note that add group colors will convert the table to a gt table. +So use this function as the last in your pipe, especially if you plan to add +more gtsummary functions in the pipe. + +## Reduced code +OK, while building our table, we've greatly reduced the code. +[CLICK] +We've removed the need to write labels inside of the table summary function and +we've removed these extra gtsummary functions as well. +And this was all done by just applying this one simple extras function. +I hope you can see why I lead-in that this one line can be really powerful. +Our code was cut in half and that was just only for one table! + +## And this is what we built +It's the same output that we were going for. +Less code, same result! + +## extras parameters +Let's discuss the default parameters for extras. +gtsummary defaults are used where applicable while adding in some sumExtras +defaults as well. +You can decide if you want to show the p-value, remove the header label, +use the emdash for clean table or pass lists for your own extras overrides +or p-value styling +[CLICK] +gtsummary defaults the overall column position in the first column. +[CLICK] +sumExtras retains all default parameter arguments for add_overall & add_p functions + +## .arg parameter +Here's how you can create reusable parameter overrides, if you wish. +[CLICK] +You can optionally create a list of default overrides for extras +[CLICK] +Let's set the p-value to false and last position to false +[CLICK] +And now set the .args parameter to use the extra args you've created. +Notice the table now does not have the p-value column and the overall column +is now in the far right location. + +## .add_p_args +We can do a similar modification to our p-values +[CLICK] +Create a p args list where +the continuous variables age and marker use the t test +and the p value output is styled to 2 digits +[CLICK] +Add that argument to the .add p args parameter within extras + +## Match gtsummary tables to gt tables +If you use both gtsummary and gt in the same report, sumExtras +has the +[CLICK] +theme_gt_compact function for gt tables. +This allows you to achieve the same compact output from the use jama theme +but for your gt tables! + +## sumExtras supports.. +Most of the gtsummary tables that you may use have either partial or Full feature +support from sumExtras. +Please check the documentation for both packages for more specifics, +or feel free to reach out to me if your tables require further customizations. + +## Thank you + +Thank you so much for choosing to spend your time with me today. +I really appreciate it. +You can find sumExtras on CRAN or from my GitHub repo. +The slides are live on my website: slides.kylegrealis.com/sumExtras +Do you love it, do you hate it, is the documentation wonky? +You can email me at kyle@kyleGrealis.com and I look forward to any feedback +you can provide! + +And thank you for spending your time with me today and +enjoy the remainder of RMed 2026! \ No newline at end of file