Files
rio/frontend/css/style.scss
2024-11-18 21:43:06 +01:00

4146 lines
87 KiB
SCSS

@use "sass:meta";
@use "switcheroos.scss";
/// For consistency, all components (and layout helper elements used for
/// alignment, scrolling, etc) share these styling attributes
@mixin shared-component-style {
position: relative;
}
/// For elements with a single child element. Stretches the child to fill the
/// parent if the parent is larger.
@mixin single-container {
display: flex;
& > * {
flex-grow: 1;
}
}
.rio-single-container {
@include single-container();
}
/// For elements with a single child element. Centers the child if the parent is
/// larger.
@mixin center-content {
display: flex;
justify-content: center;
align-items: center;
}
/// Kills the element's size request so it can't make its parent element grow.
/// Always takes the size of the nearest *positioned* parent element.
///
/// We have two implementations because they have different tradeoffs:
///
/// - `kill-size-request-with-zero-zero`: Doesn't work on "replaced elements"
/// like <svg> and <img>.
/// - `kill-size-request-with-absolute`: Can have unintended side effects (for
/// example, scroll anchoring excludes elements that are absolute)
@mixin kill-size-request-with-zero-zero {
// Prevent it from making the parent element grow
width: 0;
height: 0;
// Fill the entire parent
min-width: 100%;
min-height: 100%;
}
@mixin kill-size-request-with-absolute {
// Prevent it from making the parent element grow
position: absolute;
// Fill the entire parent
width: 100%;
height: 100%;
}
/// Kills the child's size request and adds scroll bars if necessary.
///
/// Requires 2 nested helper elements.
@mixin scroll-in-both-directions {
// The first element is responsible for scrolling
overflow: auto;
pointer-events: auto;
position: relative;
& > * {
// The 2nd element kills the content's size request
width: 0;
height: 0;
min-width: 100%;
min-height: 100%;
// For some reason horizontal scrolling forces the child's width to be
// the same as the parent's (minus the width of the scroll bar), which
// can lead to it being smaller than its content.
& > * {
min-width: max-content;
min-height: 100%;
@include single-container();
}
}
}
// Light / Dark highlight.js themes
//
// Switch between these by setting the `data-theme` attribute on the `html`
html[data-theme="light"] {
@include meta.load-css("highlightjs-default-light.css");
}
html[data-theme="dark"] {
@include meta.load-css("highlightjs-default-dark.css");
}
// Not visible here:
//
// JavaScript sets global theming variables, of the form `--rio-global-...`.
// Z-indices for components which are expected to show up on top.
//
// - `popup` MUST be above `dev-tools` in order for pop-ups in the dev tools to
// work correctly.
// - `dev-tools` must be above `dev-tools-highlighter` so they don't get greyed
// out by it
$z-index-user-root: 1;
$z-index-overlay: 2;
$z-index-dev-tools-highlighter: 3;
$z-index-dev-tools: 4;
$z-index-popup: 5;
$z-index-error-popup: 6;
// "Infinite" corner radius, for creating pill shapes
$infinite-corner-radius: 99999px;
// Nonstandard transition timing function
$transition-timing-overshoot: cubic-bezier(0.5, 0.5, 0.2, 1.14);
// Monospace fonts
$monospace-fonts: var(--rio-global-monospace-font), monospace;
@keyframes barber-pole {
from {
background-position: 0 0;
}
to {
background-position: 3rem 0rem;
}
}
@mixin barber-pole($color) {
background-image: linear-gradient(
-45deg,
$color 15%,
transparent 15%,
transparent 50%,
$color 50%,
$color 65%,
transparent 65%
);
background-size: 3rem 3rem;
animation: barber-pole 1s linear infinite;
}
// NaturalSizeObservers
.rio-natural-height-observer {
position: relative;
overflow-y: hidden;
}
.rio-natural-height-observer-flexbox {
display: flex;
flex-direction: column;
}
.rio-natural-size-observer-child-container {
@include single-container();
}
.rio-natural-size-observer-spacer {
flex-grow: 1;
}
// General
a {
color: var(--rio-local-level-2-bg);
}
a:hover {
color: var(--rio-local-level-2-bg-active);
}
code {
font-family: $monospace-fonts;
}
html {
background: var(--rio-global-background-bg);
// Fill the whole screen, at least
min-width: 100%;
min-height: 100%;
@include single-container;
}
body {
margin: 0;
padding: 0;
font-family: var(--rio-global-font, sans-serif);
@include single-container();
}
// Force input elements to use the font-family we specified. For some reason
// they don't inherit it per default.
input,
textarea,
select {
font-family: inherit;
font-size: 1rem;
}
// Scrolling helper elements
.rio-scroll-helper {
@include shared-component-style();
}
// Alignment helper elements
.rio-align-outer {
pointer-events: none;
@include shared-component-style();
}
.rio-align-inner {
position: relative;
&.stretch-child-x > * {
width: 100%;
}
&.stretch-child-y > * {
height: 100%;
}
}
// Margin helper element
.rio-margin {
pointer-events: none;
@include single-container();
box-sizing: border-box;
}
// Container elements for child components
.rio-child-wrapper {
@include single-container();
}
// All components
.rio-component {
@include shared-component-style();
}
// User-defined components
.rio-high-level-component {
@include single-container();
}
// Fundamental Root Component
.rio-fundamental-root-component {
pointer-events: none;
display: grid;
grid-template-columns: minmax(min-content, 1fr) min-content;
// The user's root component
& > .rio-user-root-container-outer {
z-index: $z-index-user-root;
grid-row: 1;
grid-column: 1;
}
&[data-has-dev-tools="true"] > .rio-user-root-container-outer {
// If the dev tools sidebar is present, we don't want the scrollbar to
// be on their left side, so we must manually take care of scrolling
// instead of letting the <html> element do it.
@include scroll-in-both-directions();
}
&[data-has-dev-tools="false"] > .rio-user-root-container-outer {
// If the dev tools sidebar is not present, we want to let the <html>
// element handle scrolling. This is important because mobile browsers
// hide the URL bar when you scroll down.
@include single-container();
& > * {
@include single-container();
& > .rio-user-root-container-inner {
@include single-container();
}
}
}
// The contents of overlays
.rio-overlays-container {
z-index: $z-index-overlay;
overflow: hidden;
}
&[data-has-dev-tools="true"] > .rio-overlays-container {
grid-row: 1;
grid-column: 1;
// We don't want overlays to cover the dev tools sidebar, so if the dev
// tools exist, overlays should only be as large as this container. In
// order for that to work, this container must be a positioned element.
position: relative;
& > * {
@include kill-size-request-with-absolute();
}
}
&[data-has-dev-tools="false"] > .rio-overlays-container {
// If there are no dev tools present, then scrolling is handled by the
// <html> element instead our rio-user-root-container, so in this case
// overlays must be `position: fixed` and cover the whole screen.
position: fixed;
width: 100%;
height: 100%;
& > * {
position: fixed;
width: 100%;
height: 100%;
}
}
// The dev tools sidebar
& > .rio-dev-tools-container {
z-index: $z-index-dev-tools;
grid-row: 1;
grid-column: 2;
@include single-container();
}
// The connection lost popup
& > .rio-connection-lost-popup-container {
z-index: $z-index-error-popup;
grid-row: 1;
grid-column: 1 / 3;
}
}
.rio-connection-lost-popup-container {
display: none;
background-color: transparent;
opacity: 0;
transition:
opacity 0.3s ease-in-out,
background-color 1s ease-in-out;
& > * {
transform: translateY(-5rem);
transition: transform 0.3s $transition-timing-overshoot;
}
}
.rio-connection-lost-popup-visible {
@include single-container(); // This also sets the display attribute
background-color: rgba(0, 0, 0, 0.5);
opacity: 1;
& > * {
transform: translateY(0);
}
}
// Dev Tools
.rio-dev-tools {
pointer-events: auto;
}
@media (width <= 50rem) or (height <= 30rem) {
.rio-dev-tools > * {
display: none !important;
}
.rio-dev-tools::after {
pointer-events: none;
position: fixed;
top: 0;
right: 0.1rem;
bottom: 0;
content: "Screen too small for Dev Tools";
color: var(--rio-global-neutral-fg);
font-size: 0.8rem;
writing-mode: vertical-rl;
text-align: center;
opacity: 0.5;
}
}
// Row & Column
.rio-linear-container {
pointer-events: none;
@include single-container();
& > * > * {
display: flex;
align-items: stretch;
// Stretch to fill the parent
min-width: 100%;
min-height: 100%;
}
}
.rio-column > * > * {
flex-direction: column;
}
// Row & Column with proportions
.rio-row.has-proportions > * {
// Cut off the spacer element
overflow-x: hidden;
width: 100%;
}
.rio-column.has-proportions > * {
// Cut off the spacer element
overflow-y: hidden;
height: 100%;
}
// Grid
.rio-grid {
pointer-events: none;
display: inline-grid;
}
// Text
.rio-text {
pointer-events: auto;
display: flex;
align-items: center;
color: var(--rio-local-text-color);
}
.rio-text > * {
flex-grow: 1;
overflow: hidden;
// Remove default styling of headings
margin: 0;
padding: 0;
}
// Class-container
.rio-class-container {
pointer-events: none;
@include single-container();
}
// Key event listener
.rio-key-event-listener {
pointer-events: none;
@include single-container();
}
// Pointer event listener
.rio-pointer-event-listener {
pointer-events: auto;
@include single-container();
}
// Rectangle
.rio-rectangle {
pointer-events: auto;
border-style: solid;
@include single-container;
// The transition time is set via JS
transition-property: background, stroke-color, stroke-width, border-radius,
shadow-color, shadow-radius, shadow-offset;
transition-timing-function: ease;
// The following attributes are controlled via variables, to allow
// JavaScript to change them, even on pseudo-classes.
background: var(--rio-rectangle-background);
backdrop-filter: var(--rio-rectangle-backdrop-filter);
-webkit-backdrop-filter: var(--rio-rectangle-backdrop-filter);
border-color: var(--rio-rectangle-stroke_color);
border-width: var(--rio-rectangle-stroke_width);
border-radius: var(--rio-rectangle-corner_radius);
box-shadow: var(--rio-rectangle-shadow_offset_x)
var(--rio-rectangle-shadow_offset_y) var(--rio-rectangle-shadow_radius)
var(--rio-rectangle-shadow_color);
box-sizing: border-box;
}
.rio-rectangle:hover {
background: var(--rio-rectangle-hover-background);
backdrop-filter: var(--rio-rectangle-hover-backdrop-filter);
-webkit-backdrop-filter: var(--rio-rectangle-hover-backdrop-filter);
border-color: var(--rio-rectangle-hover-stroke_color);
border-width: var(--rio-rectangle-hover-stroke_width);
border-radius: var(--rio-rectangle-hover-corner_radius);
box-shadow: var(--rio-rectangle-hover-shadow_offset_x)
var(--rio-rectangle-hover-shadow_offset_y)
var(--rio-rectangle-hover-shadow_radius)
var(--rio-rectangle-hover-shadow_color);
}
// Input Box: This is a style common to multiple input components, such as
// `TextInput` and `Dropdown`.
$rio-input-box-height: 2rem;
$rio-input-box-reserved-height-for-label: 1rem;
$rio-input-box-text-distance-from-bottom: 0.38rem; // To be aligned with the <input>
$rio-input-box-horizontal-padding: 0.8rem;
$rio-input-box-large-label-font-size: 1rem;
$rio-input-box-small-label-font-size: 0.8rem;
$rio-input-box-small-label-spacing-top: 0.5rem;
.rio-input-box {
pointer-events: auto;
cursor: text;
display: flex;
flex-direction: row;
align-items: stretch;
background-color: var(--rio-local-bg-variant);
transition: background-color 0.1s linear;
}
.rio-input-box-style-underlined {
border-radius: var(--rio-global-corner-radius-small)
var(--rio-global-corner-radius-small) 0 0;
}
.rio-input-box-style-rounded {
border-radius: var(--rio-global-corner-radius-small);
}
.rio-input-box-style-pill {
border-radius: $infinite-corner-radius;
}
*:not(.rio-input-box-style-underlined) > .rio-input-box-plain-bar,
*:not(.rio-input-box-style-underlined) > .rio-input-box-color-bar {
display: none;
}
.rio-input-box:hover:not(.rio-insensitive) {
background-color: var(--rio-local-bg-active);
}
.rio-input-box.rio-insensitive {
cursor: auto;
}
.rio-input-box:focus-within,
.rio-input-box.rio-input-box-focused {
outline: none;
background-color: var(--rio-local-bg-active);
}
.rio-input-box-padding {
width: $rio-input-box-horizontal-padding;
}
.rio-input-box-column {
min-height: $rio-input-box-height;
min-width: 1rem;
flex-grow: 1;
display: flex;
flex-direction: column;
align-items: stretch;
position: relative;
overflow: hidden;
}
.rio-input-box.has-label > .rio-input-box-column {
min-height: calc(
$rio-input-box-height + $rio-input-box-reserved-height-for-label
);
}
.rio-input-box-column > input,
.rio-input-box-column > textarea {
min-width: 0;
padding: 0;
background-color: transparent;
color: var(--rio-local-text-color);
caret-color: var(--rio-local-level-2-bg);
border: none;
}
.rio-input-box-column > input {
// The input element has to cover the entire height so that clicking
// anywhere will position the cursor correctly. But there is no way to
// control the vertical alignment of the text in an input element, so as a
// workaround we have to make it overflow at the bottom.
//
// The math for calculating the height is actually quite simple: At its
// natural size, with no label or extra space, the text is perfectly
// centered. We want the text to be bottom-aligned, so if there is any extra
// space, it goes on top. That means we simply have to extend the height of
// the input element by the amount of extra space and it will be centered
// again.
//
// The extra space is equal to `100% - $rio-input-box-height`, so the height
// must be `100% + 100% - $rio-input-box-height`.
//
// Note: A vertical padding can make this math incorrect. Ensure that there
// is no padding or that `box-sizing` is set to `border-box`.
position: absolute;
width: 100%;
height: calc(200% - $rio-input-box-height);
box-sizing: border-box;
}
.rio-input-box-column > textarea {
// Don't overlap with the label
//
// Padding would be better, since it's clickable, but oddly when the text is
// long, and scrolling down, the text will be visible under the label, in
// the element's padding.
margin-top: calc(
$rio-input-box-small-label-spacing-top +
$rio-input-box-small-label-font-size +
/* The bottom spacing is smaller than the top spacing, unfortunately
*/
0.3rem
);
// Don't overlap with the line at the bottom
margin-bottom: 0.1rem;
// Make the textarea exactly fill the allocated space, so wraps the text
// rather than weirdly overflowing
width: 100%;
height: 100%;
// Cut off the text instead of showing a scroll bar
overflow: hidden;
resize: none;
}
.rio-input-box-column > input:active,
.rio-input-box-column > textarea:active {
background-color: transparent;
}
.rio-input-box-column > input:focus-visible,
.rio-input-box-column > textarea:focus-visible {
outline: none;
}
.rio-input-box-label-width-reserver {
height: 0;
opacity: 0;
white-space: nowrap;
}
.rio-input-box.label-is-always-small .rio-input-box-label-width-reserver {
font-size: $rio-input-box-small-label-font-size;
}
.rio-input-box-label {
pointer-events: none;
position: absolute;
font-size: 1rem;
// Calculate the vertical position. When the InputBox is at its natural
// height, the result should be `$rio-input-box-text-distance-from-bottom`.
// When it's taller, we want to be approximately centered.
bottom: calc(50% - $rio-input-box-large-label-font-size / 2);
color: var(--rio-local-text-color);
opacity: 0.5;
transition: all 0.13s linear;
}
.rio-input-box:focus-within .rio-input-box-label,
.rio-input-box-focused .rio-input-box-label,
.rio-input-box.has-value .rio-input-box-label {
color: var(--rio-local-level-2-bg);
opacity: 1;
bottom: calc(
100% - $rio-input-box-small-label-spacing-top -
$rio-input-box-small-label-font-size
);
font-size: $rio-input-box-small-label-font-size;
}
.rio-input-box-hint-text {
// Align the text at the bottom
display: flex;
align-items: end;
padding-bottom: $rio-input-box-text-distance-from-bottom;
user-select: none;
color: var(--rio-local-text-color);
opacity: 0;
transition: all 0.13s linear;
}
.rio-input-box-prefix-text {
margin-right: 0.2rem;
}
.rio-input-box-suffix-text {
margin-left: 0.2rem;
}
.rio-input-box:focus-within > .rio-input-box-hint-text,
.rio-input-box-focused > .rio-input-box-hint-text,
.rio-input-box.has-value > .rio-input-box-hint-text {
opacity: 0.5;
}
.rio-input-box-suffix-element {
display: flex;
align-items: end;
& > * {
height: $rio-input-box-height;
}
}
.rio-input-box-plain-bar,
.rio-input-box-color-bar {
position: absolute;
bottom: 0;
height: 0.12rem;
}
.rio-input-box-plain-bar {
background-color: var(--rio-local-text-color);
left: 0;
right: 0;
opacity: 0.15;
}
.rio-input-box-color-bar {
background-color: var(--rio-local-level-2-bg);
left: 40%;
right: 40%;
opacity: 0;
transition: all 0.2s ease-in-out;
}
.rio-input-box:focus-within .rio-input-box-color-bar,
.rio-input-box-focused .rio-input-box-color-bar {
left: 0;
right: 0;
opacity: 1;
}
.rio-input-box.rio-insensitive {
--rio-local-text-color: var(--rio-global-disabled-fg);
background-color: var(--rio-global-disabled-bg);
.rio-input-box-label {
color: var(--rio-global-disabled-fg) !important;
}
}
.rio-input-box-accessibility-label {
position: absolute;
opacity: 0;
}
// Stack
.rio-stack {
pointer-events: none;
display: grid;
// Create a new stacking context, to ensure the children don't interfere
// with each other. `position` is already set by `rio-component`.
z-index: 0;
}
.rio-stack > * {
grid-row: 1;
grid-column: 1;
// Create a new stacking context, to ensure the children don't interfere
// with each other. `position` is already set by `rio-component`.
z-index: 0;
}
// Switch
.rio-switch {
pointer-events: none;
display: flex;
align-items: center;
justify-content: center;
// This reserves space for the outline so the switch doesn't change size
// when it's enabled or disabled
padding: 0.15rem;
// This div is centered in the allocated space
& > div {
pointer-events: auto;
position: relative;
width: 2.8rem;
height: 1.6rem;
background-color: var(--rio-global-disabled-bg);
border-radius: $infinite-corner-radius;
z-index: 1;
outline: 0.15rem solid var(--rio-global-disabled-bg-variant);
transition:
all 0.3s ease-in-out,
outline 0.15s linear;
}
svg {
width: 100%;
height: 100%;
margin-top: 0.1rem;
transition: fill 0.1s ease-in-out;
}
& > div > input {
position: relative;
width: 100%;
height: 100%;
padding: 0;
margin: 0;
opacity: 0;
cursor: pointer;
z-index: 3;
}
& > div > .knob {
z-index: 2;
position: absolute;
top: 0.2rem;
left: 0.2rem;
width: 1.2rem;
height: 1.2rem;
display: flex;
align-items: center;
justify-content: center;
border-radius: $infinite-corner-radius;
background-color: var(--rio-global-disabled-bg-variant);
--icon-color: transparent;
transition: all 0.3s cubic-bezier(0.2, 0.9, 0.35, 1.15);
}
&:not(.rio-switcheroo-disabled) > div > input:active + .knob {
width: 1.8rem;
}
}
.rio-switch.is-on {
& > div {
background-color: var(--rio-local-level-2-bg);
outline: 0rem solid var(--rio-global-disabled-bg-variant);
}
& > div > .knob {
left: 1.4rem;
background-color: var(--rio-local-bg);
--icon-color: var(--rio-local-level-2-bg);
}
&:not(.rio-switcheroo-disabled) > div > input:active + .knob {
left: 0.8rem;
}
}
.rio-switch.rio-switcheroo-disabled {
opacity: 0.8;
& > div {
background-color: transparent !important;
outline: 0.15rem solid var(--rio-global-disabled-bg-variant) !important;
}
& > div > input {
cursor: default !important;
}
& > div > .knob {
background-color: var(--rio-global-disabled-bg-variant) !important;
}
}
// Dropdown
.rio-dropdown {
pointer-events: auto;
display: flex;
flex-direction: column;
&:not(.rio-insensitive) > .rio-input-box {
&,
input {
cursor: pointer;
}
}
& > .rio-input-box {
flex-grow: 1;
}
// This is the element that contains the invisible copies of all options
// (which ensures that the dropdown is wide enough)
& > div:last-child {
height: 0;
overflow: hidden;
pointer-events: none;
font-weight: bold;
padding-right: 2rem; // Adds space for the arrow
}
}
.rio-dropdown-popup {
background-color: var(--rio-global-background-bg);
color: var(--rio-global-text-color);
border-radius: 0 0 var(--rio-global-corner-radius-small)
var(--rio-global-corner-radius-small);
box-shadow: 0 0 0.8rem var(--rio-global-shadow-color);
max-height: 0;
}
.rio-dropdown-popup {
transition:
max-height 0.2s ease-in-out,
box-shadow 0.2s ease-in-out;
}
.rio-dropdown-popup:not(.rio-popup-manager-open) {
max-height: 0 !important;
}
.rio-dropdown-popup-fullscreen {
position: absolute;
z-index: $z-index-popup;
left: 50%;
top: 50%;
min-width: 15rem;
transform: translate(-50%, -50%);
background-color: var(--rio-global-neutral-bg);
border-radius: var(--rio-global-corner-radius-medium);
// FIXME: This should really shade the entire screen rather than just be a
// shadow. However, after spending hours on trying - and failing - to get
// this to work, I've decided a shadow will do.
box-shadow: 0 0 0 transparent;
}
.rio-dropdown-popup-fullscreen.rio-popup-manager-open {
box-shadow: 0 0 3rem var(--rio-global-shadow-color);
}
.rio-dropdown-arrow {
pointer-events: none;
// Position the SVG
display: flex;
align-items: center;
svg {
width: 1.3rem;
height: 1.3rem;
}
}
.rio-dropdown-option-highlighted {
font-weight: bold;
color: var(--rio-local-level-2-bg);
}
.rio-dropdown-options {
position: relative;
display: flex;
flex-direction: column;
align-items: stretch;
white-space: pre;
cursor: pointer;
}
.rio-dropdown-options > div {
position: relative;
}
.rio-dropdown-options > svg {
position: relative;
width: 4rem;
height: 4rem;
margin: 1.5rem auto;
}
.rio-dropdown-option {
height: 2rem;
// Center the text vertically
display: flex;
align-items: center;
// Align the options with the text in the dropdown
padding-left: $rio-input-box-horizontal-padding;
padding-right: $rio-input-box-horizontal-padding;
}
.rio-dropdown-popup-fullscreen .rio-dropdown-option {
height: 3rem;
justify-content: center;
}
// Spawn a brightly colored element when hovered
.rio-dropdown-option::after {
content: "";
pointer-events: none;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--rio-global-primary-bg);
opacity: 0;
transition: opacity 0.1s ease-in-out;
}
.rio-dropdown-option-highlighted::after {
opacity: 0.2;
}
// Progress Bar
.rio-progress-bar {
pointer-events: auto;
position: relative;
overflow: hidden; // For the animation
}
.rio-progress-bar-track {
// We can't set a min-height on the `rio-progress-bar` because JS will
// overwrite it, so we'll set it here instead
min-height: 0.2rem;
width: 100%;
height: 100%;
background: var(--rio-local-text-color);
opacity: 0.3;
}
.rio-progress-bar-fill {
position: absolute;
height: 100%;
background: var(--rio-local-bg);
}
@keyframes rio-progress-bar-animation-indeterminate {
0% {
left: -20%;
width: 6%;
}
50% {
width: 30%;
}
100% {
left: 120%;
width: 6%;
}
}
.rio-progress-bar-indeterminate .rio-progress-bar-fill {
transform: translateX(-50%);
animation: rio-progress-bar-animation-indeterminate 1.5s ease-in-out
infinite;
}
.rio-progress-bar:not(.rio-progress-bar-indeterminate) .rio-progress-bar-fill {
left: 0;
width: var(--rio-progress-bar-fraction);
transition: width 0.3s ease-in-out;
}
// Progress Circle
.rio-progress-circle {
// Pointer events are only enabled for the filled part of the circle
pointer-events: none;
stroke: var(--rio-local-bg);
// For some reason, `align-items: center` makes the spinner grow. So we
// can't use `@include center-content()` here.
display: flex;
justify-content: center;
align-items: center;
// Since the svg inside is rotating, the corners of its bounding box will
// stick out. This can cause frequent re-layouting. To prevent this, we
// hide the overflow.
overflow: hidden;
}
.rio-progress-circle svg {
position: absolute;
max-width: 100%;
max-height: 100%;
}
.rio-progress-circle circle {
pointer-events: auto;
fill: none;
stroke-width: 3.5;
color: var(--rio-local-bg);
}
.spinning svg {
transform-origin: center;
animation: rotate 2s linear infinite;
}
.spinning .progress {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
stroke-linecap: round;
animation: dash 1.5s ease-in-out infinite;
}
.rio-progress-circle:not(.spinning) .progress {
stroke-dashoffset: -94.25;
stroke-dasharray: var(--dasharray);
transition: stroke-dasharray 0.5s ease;
}
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
@keyframes dash {
0% {
stroke-dasharray: 1, 200;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 200;
stroke-dashoffset: -35px;
}
100% {
stroke-dashoffset: -125px;
}
}
// Button
.rio-button {
pointer-events: auto;
// Preserve some colors outside of the switcheroo application, as some
// styles depend on them.
--outer-text-color: var(--rio-local-text-color);
--outer-bg-active-color: var(--rio-local-bg-active);
transition:
color 0.1s ease-in-out,
border-color 0.1s ease-in-out;
// Create a stacking context. This is needed so the `colored-text` and
// `plain-text` styles can reliably create an ::after element behind the
// text.
z-index: 0;
@include single-container;
position: relative; // For the ripple
& > * {
@include single-container;
}
}
.rio-buttonstyle-major {
background-color: var(--rio-local-bg);
box-shadow: 0 0 0 transparent;
transition:
background-color 0.1s ease-in-out,
box-shadow 0.2s ease-in-out;
}
.rio-buttonstyle-major:hover {
background-color: var(--rio-local-bg-active);
cursor: pointer;
box-shadow: 0 0.1rem 0.22rem rgba(0, 0, 0, 0.35);
}
.rio-buttonstyle-major.rio-insensitive {
cursor: default;
background-color: var(--rio-global-disabled-bg) !important;
color: var(--rio-global-disabled-fg) !important;
box-shadow: none;
}
.rio-buttonstyle-minor {
border: 0.1rem solid var(--rio-local-bg);
--rio-local-text-color: var(--rio-local-bg);
// Note the lack of transition here. While a transition would be nice, the
// text & icon wouldn't animate alongside the background, because they're
// independent high-level components. Having just the background transition
// but the foreground switch immediately is jarring.
}
.rio-buttonstyle-minor:hover {
background-color: var(--rio-local-bg);
--rio-local-text-color: var(--rio-local-fg);
cursor: pointer;
}
.rio-buttonstyle-minor.rio-insensitive {
cursor: default;
border: 0.1rem solid var(--rio-global-disabled-bg) !important;
background-color: unset !important;
--rio-local-text-color: var(--rio-global-disabled-bg) !important;
}
.rio-buttonstyle-colored-text,
.rio-buttonstyle-plain-text {
&:hover {
cursor: pointer;
position: relative;
background-color: var(--outer-bg-active-color);
}
transition: background-color 0.1s ease-in-out;
}
.rio-buttonstyle-colored-text {
--rio-local-text-color: var(--rio-local-bg);
}
.rio-buttonstyle-plain-text {
--rio-local-text-color: var(--outer-text-color);
}
.rio-buttonstyle-plain-text.rio-insensitive {
cursor: default;
--rio-local-text-color: var(--rio-global-disabled-bg);
&::after {
background-color: unset;
}
}
// Button shapes
.rio-button > * {
// Note: This also affects the ripple
border-radius: var(--border-radius);
}
.rio-shape-pill {
// Assigning an SCSS variable to a CSS variable requires special syntax.
// Without it, the CSS variable would be literally set to
// '$infinite-corner-radius' instead of the numeric value
--border-radius: #{$infinite-corner-radius};
}
.rio-shape-rounded {
--border-radius: var(--rio-global-corner-radius-small);
}
.rio-shape-rectangle {
--border-radius: 0;
}
.rio-shape-circle {
--border-radius: 50%;
}
// Icon button
.rio-icon-button {
// Pointer events are only enabled for the circle
pointer-events: none;
display: flex;
align-items: center;
justify-content: center;
.rio-button {
// Fill up the parent (helper) element
@include kill-size-request-with-absolute();
& > *:not(.rio-ripple-container) {
// Leave some padding around the icon so it doesn't stick out of the
// circle
padding: 15%;
box-sizing: border-box;
}
}
}
// Revealer
.rio-revealer {
pointer-events: auto;
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: stretch;
border-radius: var(--rio-global-corner-radius-small);
transition: background-color 0.15s ease-out;
}
.rio-revealer-header {
cursor: pointer;
display: flex;
align-items: center;
justify-content: space-between;
color: var(--rio-local-text-color);
}
.rio-revealer-label {
flex-grow: 1;
}
.rio-revealer-arrow {
transform: rotate(90deg);
transition: transform 0.25s ease-in-out;
}
.rio-revealer-open > * > .rio-revealer-arrow {
transform: rotate(0deg);
}
.rio-revealer-content-outer {
flex-grow: 1;
overflow: hidden;
transition: max-height 0.25s ease-in-out;
}
.rio-revealer-content-inner {
position: relative;
@include single-container();
opacity: 0;
transform: translateY(-50%);
transition:
opacity 0.45s ease-in-out,
transform 0.35s ease;
}
.rio-revealer-open > * > .rio-revealer-content-inner {
opacity: 1;
transform: translateY(0%);
}
// Plot
.rio-plot {
pointer-events: auto;
display: inline-block;
// Force the corner radius to be applied to the plot as well
overflow: hidden;
@include single-container();
}
.rio-plotly-plot {
position: relative;
& > div {
@include kill-size-request-with-zero-zero();
}
}
// Icon
.rio-icon {
pointer-events: auto;
svg {
@include kill-size-request-with-absolute();
}
}
// Slider
.rio-slider {
pointer-events: auto;
display: flex;
align-items: center;
--rio-slider-position-transition-time: 0.3s;
}
.rio-slider:not(.rio-switcheroo-disabled) {
cursor: pointer;
}
.rio-slider-column {
flex-grow: 1; // Fill the entire width
min-width: 3rem;
display: flex;
flex-direction: column;
}
.rio-slider-inner {
pointer-events: none;
position: relative;
height: 0.25rem;
margin-top: 0.4rem;
margin-bottom: 0.4rem;
margin-left: 0.6rem;
margin-right: 0.6rem;
}
.rio-slider-track {
position: absolute;
width: 100%;
height: 100%;
opacity: 0.3;
background: var(--rio-local-text-color);
border-radius: $infinite-corner-radius;
}
.rio-slider-fill {
position: absolute;
left: 0;
width: var(--rio-slider-fraction);
height: 100%;
background: var(--rio-local-level-2-bg);
border-radius: $infinite-corner-radius;
transition: width var(--rio-slider-position-transition-time) ease-in-out;
}
.rio-slider-glow {
position: absolute;
left: var(--rio-slider-fraction);
top: 50%;
width: 1.1rem;
height: 1.1rem;
transform: translate(-50%, -50%);
border-radius: 50%;
background-color: var(--rio-local-level-2-bg);
opacity: 0%;
transition:
left var(--rio-slider-position-transition-time) ease-in-out,
width 0.15s ease-in-out,
height 0.15s ease-in-out,
opacity 0.15s ease-in-out;
}
.rio-slider:hover:not(.rio-switcheroo-disabled) .rio-slider-glow {
width: 2.8rem;
height: 2.8rem;
opacity: 20%;
}
.rio-slider-knob {
position: absolute;
left: var(--rio-slider-fraction);
top: 50%;
width: 1.2rem;
height: 1.2rem;
transform: translate(-50%, -50%);
border-radius: 50%;
background-color: var(--rio-local-level-2-bg);
box-shadow: 0 0.1rem 0.2rem var(--rio-global-shadow-color);
transition:
left var(--rio-slider-position-transition-time) ease-in-out,
background-color 0.1s ease-in-out;
}
.rio-slider-values {
display: flex;
justify-content: space-between;
margin-top: 0.1rem;
font-size: 0.8rem;
width: 100%;
}
// Slideshow
.rio-slideshow {
pointer-events: auto;
overflow: hidden;
}
.slideshow-child-container {
position: relative;
display: grid;
width: 100%;
height: 100%;
}
.slideshow-child-container > div {
grid-column-start: 1;
grid-row-start: 1;
width: 100%;
height: 100%;
}
.slideshow-child-container > div > * {
grid-column-start: 1;
grid-row-start: 1;
width: 100%;
height: 100%;
}
.slideshow-progress {
position: absolute;
bottom: 0;
width: 100%;
height: 0.3rem;
background-color: var(--rio-local-level-2-bg);
}
// Overlay
.rio-overlay {
pointer-events: none;
}
.rio-overlay-content {
pointer-events: none;
@include single-container();
// The rest of the CSS is in fundamental-root-component
}
// Spacer
.rio-spacer {
pointer-events: none;
}
// MediaPlayer
.rio-media-player {
pointer-events: auto;
// It's not really a single-container, but this works for aligning the
// controls at the bottom ¯\_(ツ)_/¯
@include single-container();
align-items: end;
// Hide the blue outline that appears when it has keyboard focus
outline: none;
}
.rio-media-player video {
// Make it absolute so that it's behind the controls. (We don't want to make
// the controls absolute because then the media player could become too
// small for them.)
@include kill-size-request-with-absolute();
object-fit: contain;
}
.rio-media-player-alt-display {
position: absolute;
left: 50%;
top: 50%;
width: 50%;
height: 50%;
max-width: 20rem;
max-height: 20rem;
aspect-ratio: 1;
opacity: 0.5;
transform: translate(-50%, -50%);
}
.rio-media-player-controls {
background: linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.6));
padding-top: 2.5rem;
position: relative; // This puts it above the video
display: flex;
flex-direction: column;
gap: 0rem;
align-items: stretch;
transition: opacity 0.4s ease-in-out;
}
.rio-media-player-timeline {
cursor: pointer;
position: relative;
height: 2rem;
margin: 0 0.5rem;
}
.rio-media-player-timeline > div {
position: relative;
top: 50%;
height: 0.2rem;
transform: translateY(-50%);
transition: height 0.2s ease-in-out;
}
.rio-media-player-timeline:hover > div {
height: 0.4rem;
}
.rio-media-player-timeline-knob {
position: absolute;
width: 0rem;
height: 0rem;
left: 100%;
top: 50%;
background-color: var(--rio-global-primary-bg);
border-radius: 50%;
transform: translate(-50%, -50%);
transition:
width 0.2s ease-in-out,
height 0.2s ease-in-out;
}
.rio-media-player-timeline:hover .rio-media-player-timeline-knob {
width: 0.9rem;
height: 0.9rem;
}
.rio-media-player-timeline-background,
.rio-media-player-timeline-loaded,
.rio-media-player-timeline-hover,
.rio-media-player-timeline-played {
position: absolute;
width: 0%;
height: 100%;
background-color: white;
border-radius: $infinite-corner-radius;
}
.rio-media-player-timeline-background {
width: 100%;
opacity: 0.2;
}
.rio-media-player-timeline-loaded {
opacity: 0.3;
}
.rio-media-player-timeline-hover {
opacity: 0;
transition: opacity 0.2s ease-in-out;
}
.rio-media-player-timeline-played {
background-color: var(--rio-global-primary-bg);
}
.rio-media-player-controls-row {
display: flex;
gap: 1.2rem;
align-items: center;
padding: 0.5rem;
padding-top: 0;
}
.rio-media-player-button {
cursor: pointer;
width: 2rem;
height: 2rem;
}
.rio-media-player-button > img {
width: 100%;
height: 100%;
}
.rio-media-player-volume {
cursor: pointer;
position: relative;
width: 5rem;
height: 1.5rem;
}
.rio-media-player-volume > div {
position: relative;
top: 50%;
height: 0.2rem;
transform: translateY(-50%);
}
.rio-media-player-volume-background,
.rio-media-player-volume-current {
position: absolute;
width: 100%;
height: 100%;
border-radius: $infinite-corner-radius;
background-color: white;
}
.rio-media-player-volume-background {
opacity: 0.2;
}
.rio-media-player-volume-knob {
position: absolute;
width: 0.9rem;
height: 0.9rem;
left: 100%;
top: 50%;
background-color: white;
border-radius: 50%;
transform: translate(-50%, -50%);
}
.rio-media-player-volume > div {
position: relative;
top: 50%;
height: 0.2rem;
transform: translateY(-50%);
}
.rio-media-player-playtime-label {
color: white;
opacity: 0.6;
}
// ScrollContainer
//
// Scrolling is super weird. You'd think scrolling would be easy to achieve, but
// no. I don't really understand what makes it so difficult, but here are some
// of the issues I struggled with:
//
// 1. The parent element refuses to become smaller than its content even when
// scrolling is enabled
// 2. You can force the parent element to become smaller, but depending on how
// you do it, you may end up shrinking its content as well.
// 3. If multiple elements can scroll, we want the innermost one to do it (and
// not the <html> element).
// 4. When scrolling is only enabled in one direction, the parent may need to
// grow larger in the other direction in order to fit the scroll bar.
// 5. The element must have a minimum size so that the scroll bars are usable.
//
// The solution I've found requires 3 nested elements. From the outside in:
//
// 1. An element with `position: relative`, so that its child can size itself
// with `100%`.
//
// 2. This is where the CSS `overflow` setting is applied. We also override the
// child's size request by explicitly setting the `width`/`height` to the
// minimum size where the scroll bars are still usable. Finally, we force the
// element to fill its parent by setting `min-width`/`min-height` to `100%`.
//
// 3. For some reason, when an element scrolls horizontally, it overrides the
// width of its child, which can lead to the child being smaller than its
// content. This element exists solely to fix that with a `min-width:
// max-content`.
.rio-scroll-container {
pointer-events: auto;
$min-scroll-size: 5rem; // Minimum size where the scroll bar is still usable
// Set the `overflow` appropriately
&[data-scroll-x="auto"] > * {
overflow-x: auto;
}
&[data-scroll-x="always"] > * {
overflow-x: scroll;
}
&[data-scroll-y="auto"] > * {
overflow-y: auto;
}
&[data-scroll-y="always"] > * {
overflow-y: scroll;
}
// Kill the size request depending on the scroll direction
&[data-scroll-x="auto"] > *,
&[data-scroll-x="always"] > * {
width: $min-scroll-size;
min-width: 100%;
}
&[data-scroll-y="auto"] > *,
&[data-scroll-y="always"] > * {
height: $min-scroll-size;
min-height: 100%;
}
// For some reason, when scrolling is enabled in the horizontal direction,
// the child element's width gets set to the width of the scrolling element.
// This can cause it to become smaller than its content. We have to override
// this.
&[data-scroll-x="auto"] > * > *,
&[data-scroll-x="always"] > * > * {
width: max-content;
}
// Stretch to fill the parent (Note: This is the
// .rio-scroll-container-column)
& > * > * {
min-width: 100%;
min-height: 100%;
}
// 'auto'-scrolling in the y direction has a unique problem: Because the
// width of an element is decided before its height, the browser doesn't
// know whether a vertical scroll bar will be needed until it's too late. If
// it turns out that the parent didn't allocate enough width for the child
// *and* the vertical scroll bar, it will suddenly start scrolling in *both*
// directions. That's not what we want - we want to increase the parent's
// width instead.
&[data-scroll-x="never"][data-scroll-y="auto"] > * {
scrollbar-gutter: stable;
}
}
.rio-scroll-container-column {
display: flex;
flex-direction: column;
}
.rio-scroll-container-child-container {
flex-grow: 1;
overflow-anchor: none;
@include single-container();
}
.rio-scroll-container-anchor {
height: 1px;
}
// Markdown
.rio-markdown {
pointer-events: auto;
// Per default, the spacing between elements is super inconsistent. <p>
// elements have a margin on both sides, but most others (<ul>s, code
// blocks, ...) don't. So if you have two non-text elements next to each
// other, there's suddenly no spacing at all.
//
// To make things more predictable and easier to work with, we'll remove the
// margins from all direct children, but then add spacing between children.
// (This way the 1st child won't have a margin on top, and the last child
// won't have a margin on the bottom.)
//
// (Note: We have to do this not just for the markdown view itself, but any
// element that can contain multiple children. Like <li>. And... I think
// that's it?)
& > *,
li > * {
margin-top: 0;
margin-bottom: 0;
}
& > * + *,
li > * + * {
margin-top: 0.8rem;
}
li + li {
margin-top: 0.2rem;
}
// Because `overflow='ellipsize'` requires `overflow: hidden`, the bullet
// points in <ol> and <ul> elements get cut off. To fix this, we always hide
// the ::marker element (since it only supports very limited styling) and
// replace it with a ::before element that's positioned in the <li>'s
// padding.
li::marker {
font-size: 0;
}
ol,
ul {
padding-left: 0;
}
li {
padding-left: 2.2rem; // Barely enough for 3-digit <ol> counters
position: relative; // Required for ::after element positioning
box-sizing: border-box; // Required when wrapping is enabled
}
li::before {
position: absolute;
}
ol {
counter-reset: count;
}
ol > li::before {
counter-increment: count;
content: counter(count) ".";
top: 0;
right: calc(100% - 2rem);
}
ul > li::before {
content: "-";
font-weight: bold;
top: 0.1rem;
right: calc(100% - 1.7rem);
}
code {
font-family: $monospace-fonts;
background: var(--rio-local-bg-variant);
border-radius: var(--rio-global-corner-radius-medium);
padding: 0.1rem 0.3rem;
}
h1 {
font-family: var(--rio-global-heading1-font-name);
color: var(--rio-local-heading1-color);
font-size: var(--rio-global-heading1-font-size);
font-style: var(--rio-global-heading1-italic);
font-weight: var(--rio-global-heading1-font-weight);
text-decoration: var(--rio-global-heading1-text-decoration);
text-transform: var(--rio-global-heading1-all-caps);
&:not(:first-child) {
margin-top: 2rem;
}
&:not(:last-child) {
margin-bottom: 1rem;
}
}
h2 {
font-family: var(--rio-global-heading2-font-name);
color: var(--rio-local-heading2-color);
font-size: var(--rio-global-heading2-font-size);
font-style: var(--rio-global-heading2-italic);
font-weight: var(--rio-global-heading2-font-weight);
text-decoration: var(--rio-global-heading2-text-decoration);
text-transform: var(--rio-global-heading2-all-caps);
margin-top: 0;
&:not(:first-child) {
margin-top: 1.5rem;
}
&:not(:last-child) {
margin-bottom: 0.8rem;
}
}
h3 {
font-family: var(--rio-global-heading3-font-name);
color: var(--rio-local-heading3-color);
font-size: var(--rio-global-heading3-font-size);
font-style: var(--rio-global-heading3-italic);
font-weight: var(--rio-global-heading3-font-weight);
text-decoration: var(--rio-global-heading3-text-decoration);
text-transform: var(--rio-global-heading3-all-caps);
margin-top: 0;
&:not(:first-child) {
margin-top: 1rem;
}
&:not(:last-child) {
margin-bottom: 0.5rem;
}
}
p {
font-family: var(--rio-global-font);
color: var(--rio-local-text-color);
font-size: var(--rio-global-text-font-size);
line-height: 1.35em; // Purposely uses em
font-style: var(--rio-global-text-italic);
font-weight: var(--rio-global-text-font-weight);
text-decoration: var(--rio-global-text-text-decoration);
text-transform: var(--rio-global-text-all-caps);
}
// Text wrapping
&[data-overflow="nowrap"] {
p,
li,
h1,
h2,
h3,
h4,
h5,
h6 {
text-overflow: clip;
white-space: pre;
width: max-content;
}
}
&[data-overflow="wrap"] {
p,
li,
h1,
h2,
h3,
h4,
h5,
h6 {
// Kill the size request, otherwise text will pretty much never wrap
width: min-content;
min-width: 100%;
}
}
&[data-overflow="ellipsize"] {
p,
li,
h1,
h2,
h3,
h4,
h5,
h6 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
// Kill the size request, since ellipsizing is pointless if the text
// is entirely visible
width: 0;
min-width: 100%;
}
}
}
// Code Block (also used by markdown)
.rio-code-block {
pointer-events: auto;
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.5rem;
padding: var(--rio-global-corner-radius-medium);
background: var(--rio-local-bg-variant);
border-radius: var(--rio-global-corner-radius-medium);
box-sizing: border-box;
}
.rio-code-block-header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
gap: 1rem;
color: var(--rio-local-text-color);
font-size: 0.8rem;
}
.rio-code-block-language {
font-weight: bold;
opacity: 0.4;
}
.rio-code-block-copy-button {
width: 1.3rem;
height: 1.3rem;
cursor: pointer;
border: none;
background: none;
margin: 0;
padding: 0;
opacity: 0.4;
transition: opacity 0.2s ease-in-out;
}
.rio-code-block-copy-button:hover {
color: var(--rio-local-level-2-bg);
opacity: 1;
}
.rio-code-block > pre {
margin: 0;
font-size: var(--rio-global-text-font-size);
font-family: $monospace-fonts;
display: block;
}
.rio-experimental-scrolling-code-blocks .rio-code-block > pre {
overflow-x: auto;
width: 0;
min-width: 100%;
}
// Link
.rio-link {
pointer-events: auto;
cursor: pointer;
display: block;
@include single-container();
}
.rio-text-link-icon {
width: 1rem;
height: 1rem;
margin-right: 0.3rem;
}
.rio-text-link {
color: var(--rio-local-level-2-bg);
// Center the text
@include center-content();
white-space: nowrap;
}
.rio-link:not(.rio-text-link) {
// Remove the underline. For some reason it's necessary to set this on the
// link even though our text has its own `text-decoration` setting anyway
text-decoration: none;
}
// ScrollTarget
.rio-scroll-target {
pointer-events: none;
// Hide the link-copy-button unless the cursor is hovering above
& > .rio-scroll-target-url-copy-button {
pointer-events: auto;
cursor: pointer;
display: none;
}
&:hover > .rio-scroll-target-url-copy-button {
display: block;
}
}
// Color Picker
.rio-color-picker {
pointer-events: none;
display: flex;
flex-direction: column;
align-items: stretch;
}
.rio-color-picker-color-square {
pointer-events: auto;
position: relative;
min-height: 6rem;
cursor: crosshair;
margin-bottom: 0.7rem;
border-radius: var(--rio-global-corner-radius-medium);
flex-grow: 1;
}
.rio-color-picker-slider-outer {
pointer-events: auto;
position: relative;
padding: 0.7rem 0;
}
.rio-color-slider-inner {
height: 0.9rem;
cursor: crosshair;
border-radius: $infinite-corner-radius;
}
.rio-color-picker-knob {
pointer-events: none;
cursor: crosshair;
width: 1.4rem;
height: 1.4rem;
border-radius: 50%;
border: 0.2rem solid var(--rio-local-text-color);
position: absolute;
transform: translate(-50%, -50%);
box-sizing: border-box;
background: var(--chosen-color-opaque);
}
.rio-color-picker-slider-outer > .rio-color-picker-knob {
top: 50%;
}
.color-slider-checkers {
border-radius: $infinite-corner-radius;
}
.rio-color-picker-hue-bar > .rio-color-slider-inner {
background: linear-gradient(
to right,
red,
yellow,
lime,
aqua,
blue,
magenta,
red
);
}
.rio-color-picker-opacity-bar > .rio-color-slider-inner:not(.rio-checkered) {
position: absolute;
top: 0.7rem;
left: 0;
right: 0;
bottom: 0.7rem;
background: linear-gradient(
to right,
transparent,
var(--chosen-color-opaque)
);
}
.rio-color-picker-result-container {
margin-top: 0.5rem;
margin-left: auto;
margin-right: auto;
display: flex;
align-items: center;
gap: 0.8rem;
}
.rio-color-picker-selected-color-circle {
position: relative;
width: 2.5rem;
height: 2.5rem;
}
.rio-color-picker-selected-color-circle > * {
width: 100%;
height: 100%;
border-radius: 50%;
box-sizing: border-box;
border: 0.2rem solid var(--rio-local-text-color);
}
.rio-color-picker-selected-color-circle > div:first-child {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: var(--chosen-color-transparent);
}
.rio-color-picker-selected-color-circle-color {
background: var(--chosen-color-transparent);
}
.rio-color-picker-selected-color-label {
pointer-events: auto;
opacity: 0.5;
/* Fixed width to avoid movement when the value changes */
width: 5rem;
/* Text style */
color: var(--rio-local-text-color);
font-size: 0.9rem;
text-align: center;
font-weight: bold;
/* Override the default styling */
background: transparent;
border: none;
padding: 0.3rem;
border-radius: 0.5rem;
transition:
opacity 0.1s ease-in-out,
color 0.1s ease-in-out,
background-color 0.1s ease-in-out;
}
.rio-color-picker-selected-color-label:focus {
outline: none;
opacity: 1;
color: var(--rio-local-level-2-bg);
background: var(--rio-local-bg-variant);
}
.rio-checkered {
--checker-color: #888;
--checker-size: 0.9rem;
background-image: linear-gradient(
45deg,
var(--checker-color) 25%,
transparent 25%
),
linear-gradient(45deg, transparent 75%, var(--checker-color) 75%),
linear-gradient(45deg, transparent 75%, var(--checker-color) 75%),
linear-gradient(45deg, var(--checker-color) 25%, transparent 25%);
background-size: var(--checker-size) var(--checker-size);
background-position:
0 0,
0 0,
calc(var(--checker-size) * -0.5) calc(var(--checker-size) * -0.5),
calc(var(--checker-size) * 0.5) calc(var(--checker-size) * 0.5);
}
// Drawer
.rio-drawer {
pointer-events: auto;
overflow: hidden;
display: flex;
align-items: stretch;
// Create a stacking context, so the anchor content cannot interfere with
// the drawer content. `position` is already set by `rio-component`.
z-index: 0;
}
.rio-drawer-shade {
pointer-events: none;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
transition: background-color 0.3s ease-in-out;
}
.rio-drawer-anchor {
pointer-events: none;
flex-grow: 1;
// Create a stacking context, so the anchor content cannot interfere with
// the drawer content
z-index: 1;
@include single-container();
}
.rio-drawer-content-outer {
pointer-events: auto;
display: flex;
position: absolute;
background-color: var(--rio-local-bg);
// Create a stacking context, so the anchor content cannot interfere with
// the drawer content
z-index: 2;
box-shadow: 0 0 1.3rem var(--rio-global-shadow-color);
transition: transform 0.3s ease-out;
}
.rio-drawer-content-inner {
@include single-container();
}
.rio-drawer-knob {
align-self: center;
margin: 0.5rem;
border-radius: $infinite-corner-radius;
background: var(--rio-local-text-color);
opacity: 0.15;
}
.rio-drawer-left,
.rio-drawer-right {
.rio-drawer-content-inner {
overflow-y: auto;
}
.rio-drawer-knob {
width: 0.4rem;
height: 4rem;
}
}
.rio-drawer-top,
.rio-drawer-bottom {
.rio-drawer-content-inner {
overflow-x: auto;
}
.rio-drawer-knob {
width: 4rem;
height: 0.4rem;
}
.rio-drawer-content-outer {
flex-direction: column;
}
}
.rio-drawer-left .rio-drawer-knob,
.rio-drawer-top .rio-drawer-knob {
order: 1;
}
.rio-drawer-right .rio-drawer-content-inner,
.rio-drawer-bottom .rio-drawer-content-inner {
order: 1;
}
.rio-drawer-left > .rio-drawer-content-outer {
top: 0;
left: 0;
bottom: 0;
width: fit-content;
border-radius: 0 var(--rio-global-corner-radius-large)
var(--rio-global-corner-radius-large) 0;
}
.rio-drawer-right > .rio-drawer-content-outer {
top: 0;
right: 0;
bottom: 0;
width: fit-content;
border-radius: var(--rio-global-corner-radius-large) 0 0
var(--rio-global-corner-radius-large);
}
.rio-drawer-top > .rio-drawer-content-outer {
left: 0;
top: 0;
right: 0;
height: fit-content;
border-radius: 0 0 var(--rio-global-corner-radius-large)
var(--rio-global-corner-radius-large);
}
.rio-drawer-bottom > .rio-drawer-content-outer {
left: 0;
bottom: 0;
right: 0;
height: fit-content;
border-radius: var(--rio-global-corner-radius-large)
var(--rio-global-corner-radius-large) 0 0;
}
.rio-drawer-no-transition {
transition: none !important;
}
.rio-drawer-no-transition > * {
transition: none !important;
}
// Popup
.rio-popup {
pointer-events: none;
z-index: $z-index-popup;
@include single-container();
}
.rio-popup-anchor {
@include single-container();
}
.rio-popup-content {
pointer-events: auto;
@include single-container();
}
// Image
.rio-image {
pointer-events: none;
@include center-content; // Required for the error icon
img {
pointer-events: auto;
@include kill-size-request-with-absolute();
}
// Error icon
svg {
pointer-events: auto;
max-width: 3rem;
}
}
// Card
.rio-card {
pointer-events: auto;
@include single-container();
background-color: var(--rio-local-bg);
box-shadow: 0 0 0 var(--rio-global-shadow-color);
transition:
box-shadow 0.15s ease-out,
background-color 0.1s ease-out;
}
.rio-card-elevate-on-hover:hover {
box-shadow: 0 0.15rem 0.4rem var(--rio-global-shadow-color);
}
.rio-card-colorize-on-hover:hover {
background-color: var(--rio-local-bg-active);
}
// SwitcherBar
.rio-switcher-bar {
// Pointer events are only enabled for the buttons
pointer-events: none;
// Stretch the options on the main axis and center it on the other axis
display: flex;
align-items: center;
}
.rio-switcher-bar > div {
flex-grow: 1;
position: relative;
@include single-container();
}
.rio-switcher-bar-options {
flex-grow: 1;
display: flex;
align-items: stretch;
justify-content: space-between;
font-weight: bold;
}
.rio-switcher-bar-option {
pointer-events: auto;
cursor: pointer;
font-weight: bold;
display: flex;
flex-direction: column;
align-items: center;
padding: 0.5rem;
box-sizing: border-box;
border-radius: var(--rio-global-corner-radius-large);
color: var(--rio-local-text-color);
transition:
background-color 0.1s ease-out,
color 0.1s ease-out;
}
.rio-switcher-bar-option > .rio-switcher-bar-icon {
width: 1.8rem;
height: 1.8rem;
margin-bottom: 0.5rem;
fill: currentColor;
& > svg {
width: 100%;
height: 100%;
}
}
.rio-switcher-bar-option:hover {
background-color: var(--rio-local-bg-active);
}
.rio-switcher-bar-option.selected {
// This matches the primary-bg used by the marker
color: var(--rio-global-primary-fg);
}
.rio-switcher-bar-option > svg {
width: 1.8rem;
height: 1.8rem;
margin-bottom: 0.5rem;
fill: currentColor;
}
.rio-switcher-bar-option > div {
margin-left: auto;
margin-right: auto;
white-space: nowrap;
}
.rio-switcher-bar-marker {
position: absolute;
overflow: hidden;
pointer-events: none;
background: var(--rio-local-bg);
border-radius: var(--rio-global-corner-radius-large);
// Clicking too quickly sometimes ends up selecting the text, which is kinda
// ugly. Disable selection.
user-select: none;
}
.rio-switcher-bar-marker > .rio-switcher-bar-options {
position: absolute;
}
// Table
.rio-table {
pointer-events: auto;
display: grid;
border-radius: var(--rio-global-corner-radius-medium);
// Headers
& > .rio-table-header {
position: relative;
font-weight: bold;
justify-content: center;
opacity: 1 !important;
background-color: var(--rio-local-bg-variant);
}
// Row Number
& > .rio-table-row-number {
opacity: 0.5;
justify-content: end;
}
// Regular cell
& > .rio-table-cell {
justify-content: end;
}
// Non-header cells
& > .rio-table-row-number,
& > .rio-table-cell {
border-bottom: 1px solid var(--rio-local-bg-variant);
}
// All cells
& > div {
display: flex;
align-items: center;
padding: 0.5rem;
transition: background-color 0.1s ease-out;
}
}
// Lists (ListView + various list items)
.rio-list-view {
pointer-events: none;
display: flex;
flex-direction: column;
align-items: stretch;
align-content: stretch;
}
.rio-heading-list-item {
pointer-events: auto;
box-sizing: border-box;
margin-left: var(--rio-global-corner-radius-medium);
margin-top: 1rem;
margin-right: var(--rio-global-corner-radius-medium);
margin-bottom: 0.4rem;
}
.rio-separator-list-item {
pointer-events: none;
}
.rio-separator-list-item::after {
content: "";
display: block;
position: relative;
height: 1rem;
}
.rio-custom-list-item {
pointer-events: auto;
transition: background-color 0.1s ease-out;
@include single-container();
}
.rio-custom-list-item:hover {
background: var(--hover-color);
}
.rio-custom-list-item > * {
padding: 0.5rem 1rem;
}
.rio-listview-grouped {
position: relative;
pointer-events: auto;
background-color: var(--rio-local-bg-variant);
transition: background-color 0.1s ease-out;
}
.rio-listview-grouped + .rio-listview-grouped::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background-color: var(--rio-local-text-color);
opacity: 0.2;
}
.rio-list-item-ripple:hover {
background: var(--rio-local-bg-active);
}
// Flow Container
.rio-flow-container {
pointer-events: none;
@include single-container();
& > div {
display: flex;
flex-wrap: wrap;
width: min-content;
min-width: 100%;
}
}
// Traceback Popup
.rio-traceback {
pointer-events: auto;
position: relative;
margin-top: 1rem;
margin-bottom: 1rem;
padding: 2.2rem;
width: 50rem;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: stretch;
gap: 1.5rem;
border-radius: var(--rio-global-corner-radius-large);
box-shadow: 0 0.4rem 1rem var(--rio-global-shadow-color);
}
.rio-traceback-header {
display: flex;
align-items: center;
gap: 0.6rem;
}
.rio-traceback-header > svg {
width: 2.5rem;
height: 2.5rem;
fill: var(--rio-global-danger-bg);
}
.rio-traceback-header > div {
flex-grow: 1;
text-align: left;
font-size: 1.8rem;
color: var(--rio-global-danger-bg);
}
.rio-traceback-traceback {
font-family: $monospace-fonts;
font-size: 0.9rem;
white-space: pre-wrap;
overflow-wrap: break-word;
padding: 0.5rem 1rem;
background-color: var(--rio-local-bg-variant);
border-radius: var(--rio-global-corner-radius-medium);
}
.rio-traceback-footer {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.rio-traceback-footer-links {
display: flex;
flex-direction: row;
gap: 0.5rem;
justify-content: space-between;
}
.rio-traceback-footer > a {
flex: 1;
}
.rio-traceback-bold {
font-weight: bold;
}
.rio-traceback-dim {
opacity: 0.5;
}
.rio-traceback-red {
color: var(--rio-global-danger-bg);
}
.rio-traceback-yellow {
color: var(--rio-global-warning-bg);
}
// Dev Tools Connector
.rio-dev-tools-connector {
pointer-events: auto;
display: flex;
flex-direction: column;
align-items: center;
justify-content: end;
gap: 0.1rem;
z-index: 2;
text-decoration: none;
img {
width: 2.5rem;
height: 2.5rem;
object-fit: contain;
margin-bottom: 0.25rem;
}
div {
color: var(--rio-local-text-color);
transition: color 1s ease-in-out;
}
div:last-child {
margin-bottom: 1rem;
}
&:hover div {
color: var(--rio-global-secondary-fg);
transition: color 0.1s ease-in-out;
}
}
.rio-dev-tools-connector::before {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
background: linear-gradient(
to top,
var(--rio-global-secondary-bg),
transparent
);
opacity: 0;
transition: opacity 1s ease-out;
}
.rio-dev-tools-connector:hover::before {
opacity: 1;
transition: opacity 0.1s ease-out;
}
// Dev Tools Component Tree
.rio-dev-tools-component-tree {
pointer-events: auto;
overflow-x: hidden;
overflow-y: auto;
}
.rio-dev-tools-component-tree > * {
position: absolute;
width: 100%;
height: 100%;
}
.rio-dev-tools-component-tree-item {
display: flex;
flex-direction: column;
gap: 0.2rem;
}
@keyframes flash-text {
0% {
color: initial;
}
20% {
color: var(--rio-global-warning-bg);
}
100% {
color: initial;
}
}
.rio-dev-tools-component-tree-flash {
animation: flash-text 3s linear;
}
.rio-dev-tools-component-tree-item-header {
cursor: pointer;
z-index: 1;
position: relative;
padding: 0.3rem 0.6rem;
display: flex;
flex-direction: row;
align-items: center;
gap: 0.5rem;
}
// Expander Arrow
.rio-dev-tools-component-tree-item
> .rio-dev-tools-component-tree-item-header
> div:first-child {
width: 1rem;
height: 1rem;
transition: transform 0.1s ease-in-out;
}
.rio-dev-tools-component-tree-item[data-expanded="true"]
> .rio-dev-tools-component-tree-item-header
> div:first-child {
transform: rotate(90deg);
}
.rio-dev-tools-component-tree-item-header::after {
pointer-events: none;
content: "";
display: block;
z-index: -1;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: var(--rio-global-secondary-bg);
border-radius: $infinite-corner-radius;
opacity: 0;
transition: opacity 0.1s ease-in-out;
}
.rio-dev-tools-component-tree-item-header > div > svg {
width: 1rem;
height: 1rem;
}
.rio-dev-tools-component-tree-item-header-weakly-selected {
font-weight: bold;
}
.rio-dev-tools-component-tree-item-header-strongly-selected {
font-weight: bold;
color: var(--rio-global-secondary-fg);
}
.rio-dev-tools-component-tree-item-header:hover::after {
opacity: 0.4;
}
.rio-dev-tools-component-tree-item-header-weakly-selected::after {
opacity: 0.15;
}
.rio-dev-tools-component-tree-item-header-strongly-selected::after {
opacity: 0.6;
}
.rio-dev-tools-component-tree-item-header-strongly-selected:hover::after {
opacity: 0.8;
}
.rio-dev-tools-component-tree-item-children {
margin-left: 0.7rem;
display: none;
flex-direction: column;
gap: 0.2rem;
}
.rio-dev-tools-component-tree-item[data-has-children="true"][data-expanded="true"]
> .rio-dev-tools-component-tree-item-children {
display: flex;
}
.rio-dev-tools-component-highlighter {
pointer-events: none;
position: fixed;
z-index: $z-index-dev-tools-highlighter;
transition:
left 0.3s ease-in-out,
top 0.3s ease-in-out,
width 0.3s ease-in-out,
height 0.3s ease-in-out;
}
@keyframes pulse {
0% {
left: -0.3rem;
top: -0.3rem;
right: -0.3rem;
bottom: -0.3rem;
border-width: 0.4rem;
}
50% {
left: -0.7rem;
top: -0.7rem;
right: -0.7rem;
bottom: -0.7rem;
border-width: 0.3rem;
}
100% {
left: -0.3rem;
top: -0.3rem;
right: -0.3rem;
bottom: -0.3rem;
border-width: 0.4rem;
}
}
.rio-dev-tools-component-highlighter::after {
content: "";
position: absolute;
display: block;
border-radius: 1rem;
box-shadow: 0 0 0 9999rem rgba(0, 0, 0, 0.6);
border-style: solid;
border-color: var(--rio-global-secondary-bg);
animation: pulse 1.4s infinite;
}
.rio-dev-tools-background {
position: relative;
z-index: 0;
background: var(--rio-local-bg);
}
.rio-dev-tools-background > * {
z-index: 2;
}
.rio-dev-tools-background::after {
content: "";
position: absolute;
display: block;
z-index: 1;
left: 0;
top: 0;
bottom: 0;
right: 0;
background-image: linear-gradient(
-45deg,
var(--rio-local-bg) 25%,
var(--rio-global-secondary-bg) 25%,
var(--rio-global-secondary-bg) 50%,
var(--rio-local-bg) 50%,
var(--rio-local-bg) 75%,
var(--rio-global-secondary-bg) 75%,
var(--rio-global-secondary-bg)
);
background-size: 3rem 3rem;
opacity: 0.02;
}
// Dev tools component picker
.rio-component-picker {
$size: 1.8rem;
width: $size;
height: $size;
box-sizing: border-box;
padding: 0.2rem;
border-radius: 999px;
cursor: pointer;
}
.rio-component-picker:hover {
background: var(--rio-local-bg-variant);
}
.rio-component-picker.active {
background: var(--rio-local-bg-active);
}
html.picking-component * {
cursor: crosshair !important;
}
// Switcher
.rio-switcher {
pointer-events: none;
// This is not a single-container because it can briefly have two children
// during an animation. It's more like a Stack.
display: grid;
overflow: hidden;
}
.rio-switcher > * {
grid-row: 1;
grid-column: 1;
@include single-container();
opacity: 0;
transition: opacity var(--rio-switcher-transition-time) ease-in-out;
}
.rio-switcher > *.rio-switcher-active-child {
opacity: 1;
}
.rio-switcher.resizing > *:not(.rio-switcher-resizer) {
// During the resize animation, make the children absolute so they can't
// influence the switcher's size
position: absolute;
width: 100%;
height: 100%;
}
.rio-switcher-resizer {
transition:
min-width var(--rio-switcher-transition-time) ease-in-out,
min-height var(--rio-switcher-transition-time) ease-in-out;
}
// Tooltip
.rio-tooltip {
pointer-events: auto;
@include single-container();
}
.rio-tooltip-popup {
pointer-events: none;
width: min(max-content, 100vw);
padding: 0.5rem;
border-radius: var(--rio-global-corner-radius-small);
background: var(--rio-global-hud-bg);
box-shadow: 0 0.1rem 0.2rem var(--rio-global-shadow-color);
}
.rio-tooltip-popup * {
pointer-events: none !important;
}
// Build failed component
.rio-error-placeholder {
pointer-events: auto;
color: var(--rio-global-danger-fg);
display: flex;
flex-direction: column;
align-items: stretch;
background: var(--rio-global-danger-bg);
border-radius: var(--rio-global-corner-radius-medium);
// `rio-error-placeholder-content` can't have a corner radius set, because that
// would make the barber pole peek through the corners. Instead enforce
// the corner radius from this element.
overflow: hidden;
@include barber-pole(var(--rio-global-danger-bg-variant));
}
.rio-error-placeholder-top,
.rio-error-placeholder-bottom {
flex-grow: 1;
}
.rio-error-placeholder-top {
background: linear-gradient(
to top,
var(--rio-global-danger-bg),
var(--rio-global-danger-bg) 30%,
transparent
);
}
.rio-error-placeholder-bottom {
background: linear-gradient(
to bottom,
var(--rio-global-danger-bg),
var(--rio-global-danger-bg) 30%,
transparent
);
}
.rio-error-placeholder-content {
padding: 1rem;
display: flex;
flex-direction: column;
align-items: stretch;
gap: 1rem;
background: var(--rio-global-danger-bg);
}
.rio-error-placeholder-header {
align-self: center;
display: flex;
align-items: center;
gap: 0.5rem;
}
.rio-error-placeholder-icon {
width: 2rem;
height: 2rem;
}
.rio-error-placeholder-summary {
font-weight: bold;
}
.rio-error-placeholder-details {
align-self: center;
}
// Code Explorer
.rio-code-explorer {
pointer-events: none;
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
}
.rio-code-explorer-source-code {
pointer-events: auto;
display: block;
cursor: text;
white-space: pre;
font-size: 1rem;
font-family: $monospace-fonts;
position: relative;
width: min-content;
padding: 1rem;
background: var(--rio-local-bg-variant);
border-radius: var(--rio-global-corner-radius-medium);
}
.rio-code-explorer-arrow {
width: 3rem;
height: 3rem;
}
.rio-code-explorer-build-result {
position: relative;
}
.rio-code-explorer-build-result > *:not(.rio-code-explorer-highlighter) {
position: relative !important;
}
.rio-code-explorer-highlighter {
pointer-events: none;
position: absolute;
opacity: 0;
transition:
opacity 0.3s ease-in-out,
left 0.3s ease-in-out,
top 0.3s ease-in-out,
width 0.3s ease-in-out,
height 0.3s ease-in-out;
}
.rio-code-explorer-highlighter::after {
content: "";
z-index: $z-index-dev-tools-highlighter;
position: absolute;
border-radius: 1rem;
border-style: solid;
border-width: 0.2rem !important;
border-color: var(--rio-global-secondary-bg);
box-shadow: 0 0 4rem 1rem var(--rio-global-secondary-bg);
opacity: 0.4;
animation: pulse 1.4s infinite;
}
// Separator
.rio-separator {
pointer-events: none;
background-color: var(--rio-local-bg);
// @single-container doesn't work with our ::after element, so we'll inline
// it
display: flex;
align-items: stretch;
}
.rio-separator::after {
content: "";
flex-grow: 1;
// We can't set the min-size on the separator element itself because JS will
// overwrite it with the value from the backend, so we'll do it here
$min-size: 1px;
min-width: $min-size;
min-height: $min-size;
// This creates a lighter shade of the text color without being see-through
background-color: var(--separator-color);
opacity: var(--separator-opacity);
}
// Ripple Effect
.rio-ripple-container {
pointer-events: none;
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
}
.rio-ripple-effect {
// background: radial-gradient(
// circle at center center,
// transparent 5%,
// var(--rio-ripple-color) 40%,
// transparent 70%
// );
position: absolute;
background: var(--rio-ripple-color);
border-radius: 50%;
transform: translate(-50%, -50%);
transition:
top var(--rio-ripple-duration),
left var(--rio-ripple-duration),
width var(--rio-ripple-duration),
height var(--rio-ripple-duration),
opacity var(--rio-ripple-duration);
}
// Popup Manager (NOT the `Popup` component!)
.rio-popup-manager-content {
position: fixed;
z-index: $z-index-popup;
width: min-content;
height: min-content;
}
.rio-popup-animation-scale {
transform: scale(0);
opacity: 0;
transition:
transform 0.2s linear,
opacity 0.1s ease-in-out;
}
.rio-popup-animation-scale.rio-popup-manager-open {
transform: scale(1);
opacity: 1;
transition:
transform 0.2s $transition-timing-overshoot,
opacity 0.1s ease-in-out;
}
// Calendar
.rio-calendar {
pointer-events: auto;
@include center-content;
}
.rio-calendar-inner {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.rio-calendar-header {
display: flex;
flex-direction: row;
gap: 0.2rem;
flex: 0;
& > :nth-child(3) {
pointer-events: none;
display: flex;
justify-content: center;
align-items: center;
flex-grow: 1;
}
}
.rio-calendar-button {
position: relative;
width: 1.6rem;
height: 1.6rem;
cursor: pointer;
color: var(--rio-local-text-color);
}
.rio-calendar-button:hover {
color: var(--rio-local-level-2-bg);
}
.rio-calendar-button:hover::after {
content: "";
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: var(--rio-local-level-2-bg);
border-radius: $infinite-corner-radius;
opacity: 0.1;
}
.rio-calendar-grid {
display: grid;
grid-template-columns: repeat(7, 1.8rem);
grid-template-rows: repeat(7, 1.8rem);
gap: 0.5rem;
}
.rio-calendar-day-name {
font-weight: bold;
text-align: center;
}
.rio-calendar-day {
position: relative;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.rio-calendar-day-other-month {
opacity: 0.4;
}
.rio-calendar-day:hover::after {
content: "";
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: var(--rio-local-text-color);
border-radius: $infinite-corner-radius;
opacity: 0.1;
}
.rio-calendar-selected-day {
font-weight: bold;
color: var(--rio-local-level-2-fg);
background: var(--rio-local-level-2-bg);
border-radius: $infinite-corner-radius;
}
// Layout Display
.rio-layout-display {
pointer-events: none;
@include center-content;
}
.rio-layout-display-parent {
pointer-events: auto;
position: relative;
z-index: 0;
background-color: var(--rio-global-neutral-bg-variant);
border-radius: var(--rio-global-corner-radius-small);
}
.rio-layout-display-margin {
position: absolute;
z-index: 1;
background-color: var(--rio-global-neutral-fg);
border-radius: var(--rio-global-corner-radius-small);
opacity: 0.15;
}
.rio-layout-display-child {
position: absolute;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
color: var(--rio-global-hud-fg);
text-overflow: ellipsis;
overflow: hidden;
background-color: var(--rio-global-hud-bg);
border-radius: var(--rio-global-corner-radius-small);
opacity: 0.5;
// Disable text selection, as that changes the cursor and is generally
// annoying in this context
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
transition:
background-color 0.1s ease-out,
box-shadow 0.15s ease-out;
}
.rio-layout-display-child:not(.rio-layout-display-target) {
cursor: pointer;
}
.rio-layout-display-child:not(.rio-layout-display-target):hover {
background-color: var(--rio-global-hud-bg-active);
box-shadow: 0 0.15rem 0.3rem var(--rio-global-shadow-color);
}
.rio-layout-display-target {
color: var(--rio-global-secondary-fg) !important;
background-color: var(--rio-global-secondary-bg) !important;
opacity: 1 !important;
}
// Checkbox
.rio-checkbox {
pointer-events: none;
@include center-content();
}
.rio-checkbox > input {
pointer-events: auto;
cursor: pointer;
position: absolute;
width: 1.4rem;
height: 1.4rem;
margin: 0;
padding: 0;
border-width: 0;
opacity: 0;
}
.rio-checkbox.rio-insensitive > input {
pointer-events: none;
cursor: default;
}
.rio-checkbox-border {
border-width: 0.15rem;
border-style: solid;
border-color: var(--border-color);
border-radius: 0.2rem;
box-sizing: border-box;
background-color: transparent;
opacity: 0.5;
width: 1.4rem;
height: 1.4rem;
transition:
opacity 0.2s ease-in-out,
border-width 0.2s ease-in-out,
border-color 0.2s ease-in-out,
background-color 0.2s ease-in-out;
}
.rio-checkbox.is-on .rio-checkbox-border {
border-width: 0.7rem;
opacity: 1;
border-color: var(--rio-local-level-2-bg);
background-color: var(--rio-local-level-2-bg);
}
.rio-checkbox-check {
position: absolute;
width: 1.8rem;
height: 1.8rem;
transform: scale(0);
transition: transform 0.2s ease-in-out;
}
.rio-checkbox.is-on .rio-checkbox-check {
transform: scale(1);
}
// Html
.rio-html {
// Disable pointer events, the user can always turn them back on if desired
pointer-events: none;
@include single-container(); // FIXME: Should we do this?
}
// Dialogs
.rio-dialog-container {
pointer-events: none;
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: $z-index-popup;
background-color: transparent;
opacity: 0;
// Theses durations are also referenced in code!
transition:
opacity 0.2s ease-in-out,
background-color 0.5s ease-in-out;
& > * {
transform: translateY(-2rem);
transition: transform 0.2s $transition-timing-overshoot;
}
@include single-container();
}
.rio-dialog-container-enter {
background-color: rgba(0, 0, 0, 0.5);
opacity: 1;
& > * {
transform: translateY(0);
}
}
// Website
.rio-website {
pointer-events: auto;
}
// Upload Area
.rio-file-picker-area {
pointer-events: auto;
position: relative;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: start;
// To round the progress indicator
overflow: hidden;
color: var(--rio-local-fg);
background-color: var(--rio-local-bg-variant);
border-radius: var(--rio-global-corner-radius-medium);
}
.rio-file-picker-area:not(.rio-file-picker-area-file-hover):hover {
background-color: var(--rio-local-bg-active);
}
.rio-file-picker-area-child-container {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
}
.rio-file-picker-area-icon {
width: 3.5rem;
height: 3.5rem;
}
.rio-file-picker-area-column {
flex-grow: 1;
display: flex;
flex-direction: column;
gap: 0.3rem;
}
.rio-file-picker-area-file-types {
opacity: 0.5;
}
.rio-file-picker-area-button > div {
color: var(--rio-local-text-color);
padding: 0.5rem 1rem;
font-weight: bold;
}
.rio-file-picker-area-progress {
pointer-events: none;
position: absolute;
left: 0rem;
top: 0;
bottom: 0;
border-radius: 50%;
opacity: 0;
transition: opacity 0.3s;
}
.rio-file-picker-area-progress::after {
content: "";
display: block;
position: relative;
width: var(--progress);
height: 100%;
background: var(--rio-local-level-2-bg);
// This would be nice, but also causes the progress bar to animate when
// receding back to 0.
//
// transition: width 0.3s ease-in-out;
}
.rio-file-picker-area-file-hover {
cursor: copy;
color: var(--rio-local-level-2-bg);
}
.rio-file-picker-area::before {
pointer-events: none;
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: var(--rio-global-corner-radius-medium);
background: radial-gradient(
circle at var(--x) var(--y),
var(--rio-local-level-2-bg),
var(--rio-local-bg-active) 30rem
);
opacity: 0;
transition: opacity 0.3s;
}
.rio-file-picker-area-file-hover::before {
opacity: 0.2;
}
.rio-file-picker-area > input {
display: none;
}
// Graph Editor
$graph-editor-node-padding: 0.7rem;
$graph-editor-port-size: 1.4rem;
.rio-graph-editor {
pointer-events: auto;
}
.rio-graph-editor > * {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
.rio-graph-editor::after {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
background-image: radial-gradient(
circle,
var(--rio-local-fg) 12%,
transparent 12%
);
background-size: 1.3rem 1.3rem;
opacity: 0.15;
& > * {
position: absolute;
width: 100%;
height: 100%;
}
}
.rio-graph-editor-selection {
pointer-events: none;
position: absolute;
z-index: 1;
left: 10rem;
top: 20rem;
width: 30rem;
height: 40rem;
border: 0.15rem solid var(--rio-global-primary-bg);
border-radius: var(--rio-global-corner-radius-small);
transition: opacity 0.2s ease-in-out;
}
.rio-graph-editor-selection::after {
content: "";
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
box-shadow: inset 0 0 15rem 0 var(--rio-global-primary-bg);
border-radius: var(--rio-global-corner-radius-small);
opacity: 0.2;
}
.rio-graph-editor-node {
pointer-events: auto;
display: flex;
flex-direction: column;
align-items: stretch;
position: absolute;
min-width: 8rem;
background-color: var(--rio-global-secondary-bg);
border-radius: var(--rio-global-corner-radius-small)
var(--rio-global-corner-radius-small)
var(--rio-global-corner-radius-medium)
var(--rio-global-corner-radius-medium);
outline: 0 solid var(--rio-global-primary-bg);
transition:
background-color 0.1s ease-in-out,
box-shadow 0.1s ease-in-out,
outline 0.1s $transition-timing-overshoot;
&:has(.rio-graph-editor-node-header:hover) {
background-color: var(--rio-global-secondary-bg-active);
box-shadow: 0 0.2rem 0.6rem var(--rio-global-shadow-color);
}
}
.rio-graph-editor-node-header {
pointer-events: auto;
user-select: none;
font-weight: bold;
text-align: center;
padding: 0.3rem $graph-editor-node-padding 0.3rem $graph-editor-node-padding;
cursor: move;
color: var(--rio-global-secondary-fg);
}
.rio-graph-editor-node-body {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 0.5rem;
padding: $graph-editor-node-padding;
border-radius: var(--rio-global-corner-radius-small);
background-color: var(--rio-local-bg);
}
.rio-graph-editor-selected-node {
border-radius: var(--rio-global-corner-radius-small);
outline: 0.15rem solid var(--rio-global-primary-bg);
}
.rio-graph-editor-port {
position: relative;
}
.rio-graph-editor-port-circle {
position: absolute;
pointer-events: auto;
cursor: pointer;
width: $graph-editor-port-size;
height: $graph-editor-port-size;
aspect-ratio: 1/1;
border-radius: 50%;
}
.rio-graph-editor-input {
text-align: left;
}
.rio-graph-editor-output {
text-align: right;
}
.rio-graph-editor-input > .rio-graph-editor-port-circle {
left: 0;
transform: translateX(
calc(-1 * ($graph-editor-port-size / 2 + $graph-editor-node-padding))
);
}
.rio-graph-editor-output > .rio-graph-editor-port-circle {
right: 0;
transform: translateX(
calc($graph-editor-port-size / 2 + $graph-editor-node-padding)
);
}
.rio-graph-editor-port-circle::after {
content: "";
position: absolute;
left: 25%;
top: 25%;
right: 25%;
bottom: 25%;
border-radius: 50%;
background-color: var(--port-color);
transition:
left 0.1s ease-in-out,
top 0.1s ease-in-out,
right 0.1s ease-in-out,
bottom 0.1s ease-in-out;
}
.rio-graph-editor-port-circle:hover::after {
left: 15%;
top: 15%;
right: 15%;
bottom: 15%;
}
.rio-graph-editor-port-text {
pointer-events: none;
}