Styling
Ripple supports native CSS styling that's scoped (localized) to the given component using the <style> element.
component MyComponent() {
<div class="container">
<h1>{'Hello World'}</h1>
</div>
<style>
.container {
background: blue;
padding: 1rem;
}
h1 {
color: white;
font-size: 2rem;
}
</style>
}The <style> element must be top-level within a component. :::
Dynamic Classes
In Ripple, the class attribute can accept more than just a string — it also supports objects and arrays. Truthy values are included as class names, while falsy values are omitted. This behavior is powered by the clsx library.
Examples:
let includeBaz = #ripple.track(true);
<div class={{ foo: true, bar: false, baz: @includeBaz }} />
// becomes: class="foo baz"
<div class={['foo', { baz: false }, 0 && 'bar', [true && 'bat']]} />
// becomes: class="foo bat"
let count = #ripple.track(3);
<div class={['foo', { bar: @count > 2 }, @count > 3 && 'bat']} />
// becomes: class="foo bar"Dynamic Inline Styles
Sometimes you might need to dynamically set inline styles. For this, you can use the style attribute, passing either a string or an object to it:
let color = #ripple.track('red');
<div style={`color: ${@color}; font-weight: bold; background-color: gray`} />
<div style={{ color: @color, fontWeight: 'bold', 'background-color': 'gray' }} />
const style = {
@color,
fontWeight: 'bold',
'background-color': 'gray',
};
// using object spread
<div style={{ ...style }} />
// using object directly
<div {style} />Both examples above will render the same inline styles, however, it's recommended to use the object notation as it's typically more performance optimized.
When passing an object to the style attribute, you can use either
camelCase or kebab-case for CSS property names. :::
Global Styles
By default, all styles in Ripple are scoped to the component. To apply global styles, use the :global() pseudo-class or :global block:
export component App() {
<div class="container">
<Child />
</div>
<style>
/* Scoped to Parent only */
.container {
padding: 1rem;
}
/* Global - Not Recommended - applies to any .highlight in any component */
:global(.highlight) {
color: red;
font-weight: bold;
}
/* Global: - Recommended - scoped parent with global child selector */
.container :global(.nested) {
margin-left: 2rem;
}
/* Global block - everything inside is global */
div :global {
.header {
font-size: 3rem;
}
}
</style>
}
component Child() {
// The div should have its font-size at 2rem from parent
<div>
<h2 class="header">{'This is a header with font-size 3rem'}</h2>
<span class="highlight">{'This will be red and bold'}</span>
<p class="nested">{'This will have left margin'}</p>
</div>
}Global Keyframes
Keyframes are scoped by default. To create global keyframes that can be shared across components, prefix the animation name with -global-:
export component App() {
<div class="parent">
<Child />
</div>
<style>
/* Scoped keyframe - only usable within Parent */
@keyframes slideIn {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
/* Global keyframe - usable in any component */
@keyframes -global-fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.parent {
animation: slideIn 1s;
}
</style>
}
component Child() {
<div class="child">{'Child content'}</div>
<style>
.child {
animation: fadeIn 1s; /* Uses global fadeIn from Parent */
}
</style>
}Passing Scoped Classes to Child Components (#ripple.style)
Scoped styles only apply to DOM elements within the same component. If you want a parent to influence how a child component looks, you can pass scoped class names as props using the #ripple.style identifier (previously #style).
#ripple.style.className produces a string containing both the CSS scope hash and the class name (e.g. "ripple-abc123 highlight"), which the child applies to its own elements via the class attribute.
#style (legacy) and #ripple.style are interchangeable. New code
should use #ripple.style for consistency with the rest of the #ripple.* namespace. :::
Basic Usage
component Child({ className }: { className: string }) {
<div class={className}>{'styled child'}</div>
}
component Parent() {
<Child className={#ripple.style.highlight} />
<style>
.highlight {
color: red;
}
</style>
}You can pass multiple classes:
component Child({ primary, secondary }: { primary: string; secondary: string }) {
<div class={primary}>{'primary'}</div>
<span class={secondary}>{'secondary'}</span>
}
component Parent() {
<Child primary={#ripple.style.primary} secondary={#ripple.style.secondary} />
<style>
.primary {
color: blue;
}
.secondary {
color: gray;
}
</style>
}With Dynamic Components
#ripple.style also works when rendering dynamic components with <@Component />:
component Child({ cls }: { cls: string }) {
<span class={cls}>{'text'}</span>
}
component Parent() {
let Dynamic = #ripple.track(() => Child);
<@Dynamic cls={#ripple.style.text} />
<style>
.text {
color: red;
}
</style>
}Combining Parent and Child Styles
A child component can combine classes it receives from a parent with its own scoped classes:
component Card({ className }: { className?: string }) {
<div class={['card-base', className ?? '']}>{'card content'}</div>
<style>
.card-base {
border: 1px solid black;
}
</style>
}
component App() {
<Card className={#ripple.style.themed} />
<style>
.themed {
background: purple;
}
</style>
}Standalone Requirement
A class referenced via #style must exist as a standalone selector in the <style> block. Classes that only appear inside compound, descendant, or combinator selectors cannot be passed.
If a class appears both standalone and in a descendant selector, it can still be used with #style:
component App() {
<div class="parent">
<Child cls={#ripple.style.dual} />
</div>
<style>
/* ✅ Standalone rule — makes .dual valid for #style */
.dual {
color: blue;
}
/* Also applies when .dual is inside .parent */
.parent .dual {
font-weight: bold;
}
</style>
}The following will not work because the class has no standalone rule:
// ❌ .nested only exists in a descendant selector
component App() {
<Child cls={#ripple.style.nested} />
<style>
.wrapper .nested {
color: red;
}
</style>
}Syntax Rules
- Dot notation:
#ripple.style.className - Bracket notation:
#ripple.style['className'](static string only) - No dynamic access:
#ripple.style[variable]is a compile error - Components only:
#ripple.stylecan only be used inside acomponentbody - Props only:
#ripple.stylecannot be used directly on DOM elements — pass it to a child component instead
Legacy syntax: #ripple.style.className and #ripple.style['className'] are still
accepted as aliases for #ripple.style.*. :::
