Questa pagina è stata tradotta automaticamente e può contenere errori. Leggi l'originale in inglese

Documentazione

Stile

Ogni elemento è stilizzato con gli stessi stili nativi, scritti in due modi. In Rust, gli stili sono metodi tipizzati concatenati sull’elemento (.p_4(), .rounded(px(12.0)), .bg(color)); in TSX, una prop style di proprietà in camelCase, oppure classi Tailwind. Entrambi includono funzionalità CSS come box shadow, gradienti, filtri, outline, transizioni e grid, e alla vista nativa vengono inviate solo le proprietà modificate.

guide-styling.tsx
guide-styling.tsxLive
import { mount, Scroll, signal, Text, View } from "nsh";
import { reduceMotion } from "nsh";
import { desktop, focusRing, size, tint, ui, wide } from "./palette";

// A function, so the styles follow the page's theme.
const styles = () =>
  ({
    tile: {
      width: 96,
      height: 96,
      borderRadius: 16,
      alignItems: "center",
      justifyContent: "center",
      backgroundColor: ui().card.backgroundColor,
    },
    label: { fontSize: 12, fontWeight: "600", color: ui().text, textAlign: "center" },
    selected: { borderWidth: 3, borderColor: ui().accent },
  }) as const;

/** `ink`: the label's colour on a filled tile. */
function Tile(props: { label: string; style?: object; ink?: string }) {
  return (
    <View style={[styles().tile, props.style]}>
      <Text style={[styles().label, props.ink !== undefined && { color: props.ink }]}>{props.label}</Text>
    </View>
  );
}

function App() {
  const on = signal(false);
  const picked = signal(1);

  const tiles = (justify: "center" | "flex-start") => (
    <View style={{ flexDirection: "row", flexWrap: "wrap", gap: 16, justifyContent: justify }}>
      <Tile
        label="boxShadow"
        style={{ backgroundImage: ui().card.backgroundImage, boxShadow: ui().card.boxShadow }}
      />
      <Tile label="dashed border" style={{ borderWidth: 2, borderStyle: "dashed", borderColor: ui().violet }} />
      <Tile label="gradient" ink="#0c0d0f" style={{ backgroundImage: "linear-gradient(135deg, #dbf893, #7ea8ff)" }} />
      <Tile label="radial" ink="#0c0d0f" style={{ backgroundImage: "radial-gradient(circle, #ffd99a, #ff8a7a)" }} />
      <Tile label="corners" style={{ borderTopLeftRadius: 40, borderBottomRightRadius: 40, backgroundColor: tint(ui().up, 0.3) }} />
      <Tile label="continuous" ink={ui().page} style={{ borderRadius: 28, borderCurve: "continuous", backgroundColor: ui().up }} />
      <Tile label="rotate + scale" style={{ backgroundColor: tint(ui().info, 0.3), rotate: "-8deg", scale: 0.9 }} />
      <Tile label="outline" style={{ outlineWidth: 2, outlineColor: ui().accent, outlineOffset: 3 }} />
      <Tile label="opacity 0.5" ink={ui().page} style={{ backgroundColor: ui().text, opacity: 0.5 }} />
    </View>
  );
  const arrays = () => (
    <View style={{ gap: 10 }}>
      <Text style={{ fontSize: 13, fontWeight: "600", color: ui().muted }}>STYLE ARRAYS: {desktop ? "CLICK" : "TAP"} TO SELECT</Text>
      <View style={{ flexDirection: "row", gap: 10 }}>
        <Each of={["Small", "Medium", "Large"]}>
          {(label, i) => (
            <View
              onPress={() => picked.set(picked() === i() ? -1 : i())}
              style={[
                desktop
                  ? { height: size.control + 6, justifyContent: "center", paddingHorizontal: 14, borderRadius: 8, backgroundColor: ui().raised.backgroundColor, alignItems: "center", borderWidth: 3, borderColor: "transparent" }
                  : { flex: 1, padding: 12, borderRadius: 10, backgroundColor: ui().raised.backgroundColor, alignItems: "center", borderWidth: 3, borderColor: "transparent" },
                picked() === i() && styles().selected,
              ]}
              hoverStyle={{ opacity: 0.88 }}
              focusStyle={focusRing()}
              activeStyle={{ opacity: 0.7 }}
            >
              <Text style={{ color: ui().text, fontSize: desktop ? 13 : undefined }}>{label()}</Text>
            </View>
          )}
        </Each>
      </View>
    </View>
  );
  const transitions = () => (
    <View style={{ gap: 10 }}>
      <Text style={{ fontSize: 13, fontWeight: "600", color: ui().muted }}>TRANSITIONS: {desktop ? "CLICK" : "TAP"} THE BOX</Text>
      <View
        onPress={() => on.set(!on())}
        style={{
          height: 72,
          borderRadius: on() ? 36 : 12,
          backgroundColor: on() ? ui().accent : ui().info,
          translate: on() ? 120 : 0,
          width: 150,
          alignItems: "center",
          justifyContent: "center",
          transitionProperty: "all",
          transitionDuration: reduceMotion() ? 0 : 400,
          transitionTimingFunction: "ease-in-out",
        }}
        hoverStyle={{ opacity: 0.88 }}
        focusStyle={focusRing()}
        activeStyle={{ opacity: 0.7 }}
      >
        <Text style={{ color: ui().page, fontWeight: "600" }}>{on() ? "On" : "Off"}</Text>
      </View>
    </View>
  );

  return (
    <Scroll style={{ flex: 1, backgroundColor: ui().page }}>
      {() =>
        wide() ? (
          <View style={{ padding: 20, paddingTop: 24, flexDirection: "row", gap: 32 }}>
            <View style={{ width: 5 * 96 + 4 * 16 }}>{tiles("flex-start")}</View>
            <View style={{ flex: 1, minWidth: 0, gap: 24 }}>
              {arrays()}
              {transitions()}
            </View>
          </View>
        ) : (
          <View style={{ padding: 20, paddingTop: 24, gap: 20 }}>
            {tiles("center")}
            {arrays()}
            {transitions()}
          </View>
        )
      }
    </Scroll>
  );
}

mount(App);

I tre modi scrivono un unico insieme di proprietà e hanno lo stesso significato. I nomi sono quelli del CSS (alcuni usano i nomi più semplici che le app scrivono, come paddingHorizontal, marginStart e numberOfLines). I valori CSS (colori, gradienti, filtri, ombre, trasformazioni, parole chiave di allineamento) e le utility Tailwind hanno il significato che hanno sul web, con ogni metodo Rust e ogni utility che imposta ciò che Tailwind CSS 4 genera per esso, quindi ciò che conosci dal web vale anche qui: paddingLeft è padding-left e pl-4, boxShadow è box-shadow, display: "flex" è una riga come lo è la classe flex. Il resto è pensato per le app native: le lunghezze sono in punti (16, "50%", "auto"; le classi accettano rem e le altre unità del CSS), i valori predefiniti sono quelli del layout nativo (elencati più avanti), un valore che cambia accetta numeri invece di una stringa (translate: [x, 0], i livelli di un’ombra), e ciò che le piattaforme offrono oltre il CSS ha estensioni proprie.

view()
    .p_4()
    .rounded(px(12.0))
    .bg(white())
    .border_1()
    .border_color(rgb(0xd0d7de))
    .shadow([BoxShadow {
        offset_y: px(4.0),
        blur: px(12.0),
        color: rgba(0x0000001f).into(),
        ..BoxShadow::default()
    }])
    .child(
        text("Card")
            .text_size(px(17.0))
            .font_weight(FontWeight::SEMIBOLD)
            .text_color(rgb(0x1f2328)),
    )

Questa pagina descrive come si combinano gli stili e le proprietà visive. Le proprietà di layout (flexbox, grid, dimensioni, posizioni) sono in Layout, le proprietà del testo in Testo, e ogni proprietà con il suo tipo è elencata nel riferimento degli stili.

#Combinare gli stili

In Rust, gli stili condivisi sono funzioni che prendono un elemento e lo restituiscono stilizzato, e le condizioni sono semplice Rust: .when(condition, |v| ..) per ciò che è noto quando l’elemento viene creato, .style_when(signal, |s| ..) per gli stili che cambiano mentre l’app è in esecuzione. In TSX, style accetta un oggetto, o un array di oggetti uniti da sinistra a destra; le voci falsy (false, null, undefined) vengono ignorate, il che rende brevi gli stili condizionali:

fn button(danger: bool, busy: Signal<bool>) -> View {
    view()
        .px_4()
        .py(px(10.0))
        .rounded(px(10.0))
        .bg(rgb(0x0969da))
        .when(danger, |v| v.bg(rgb(0xcf222e)))
        .style_when(busy, |s| s.opacity(0.4))
}

In TSX, definisci gli stili condivisi come costanti fuori dai componenti (con as const, così TypeScript mantiene i tipi letterali come "row"). Basta un semplice oggetto: gli stili vengono analizzati in Rust ogni volta che vengono impostati, a partire da oggetti che nessuno deve registrare.

Uno stile che legge dei signal li segue: in Rust, passa al metodo una closure (.bg(move || ..)), e solo quella proprietà viene impostata di nuovo quando i signal cambiano; in TSX, un’espressione di stile che legge dei signal viene ricostruita, e NSH invia solo le proprietà i cui valori sono diversi:

view()
    .p_3()
    .bg(move || if selected.get() { rgb(0xddf4ff) } else { rgb(0xffffff) })

Quando un elemento TSX ha entrambi, style prevale su class: le classi vengono applicate per prime, poi lo stile inline sopra di esse. Una chiave impostata a undefined non imposta nulla (backgroundColor: active ? accent : undefined mantiene il colore della classe quando è inattivo); null imposta il valore predefinito. In Rust, un metodo successivo prevale su uno precedente per la stessa proprietà.

All’interno di un singolo stile (un oggetto, o un array unito in uno solo), l’ordine delle chiavi non fa differenza: una proprietà più specifica prevale sulla sua forma abbreviata, borderTopWidth su borderWidth, borderLeftColor su borderColor, marginTop su marginVertical su margin (lo stesso vale per il padding), rowGap su gap, flexGrow su flex, gridColumnStart su gridColumn. Una proprietà specifica impostata a null in quel contesto mostra il valore della forma abbreviata.

#Unità e valori

  • Le lunghezze sono in punti (pixel indipendenti dalla densità): px(100.0) in Rust e width: 100 in TSX valgono 100pt, cioè 200 o 300 pixel fisici su uno schermo 2× o 3×.
  • Le percentuali (relative(0.5) in Rust, "50%" in TSX) sono relative al genitore, per dimensioni, posizioni, margini e padding (Layout).
  • Gli angoli sono deg(45.0), radians(0.5) o turn(0.25) in Rust, e gradi come numeri o stringhe con unità in TSX: 45, "45deg", "0.5rad"; rotate e transform del CSS accettano anche "0.25turn", e all’interno di una stringa transform ogni angolo ha la sua unità.
  • Le durate sono in millisecondi: .duration(150.0) in Rust, numeri o stringhe ("150ms", "0.2s") in TSX.

#Valori predefiniti

Dove nessuno stile imposta una proprietà, si applicano i valori predefiniti di NSH:

Valore predefinito
Layout dei figli di una vistauna colonna (un contenitore flex con flexDirection: "column"); display: "flex" crea una riga a meno che flexDirection sia impostato, come in CSS
flexShrink0 (i figli non si restringono se non richiesto)
alignContent"flex-start" (nelle grid, "normal" del CSS)
position"relative"
minWidth / minHeight0
backgroundColortrasparente
Testonero, 14pt, peso 400
Colore di bordo e outlineil colore del testo (currentColor del CSS)

Le nuove viste non hanno alcuno stile finché non gliene assegni uno, quindi i wrapper senza stile non hanno costo sul lato nativo.

#Colori

In Rust, i colori sono valori tipizzati: rgb(0x0099ff), rgba(0x0099ffcc), hsla(..), white(), black(), e la palette di Tailwind come nsh_core::element::palette (BLUE.shade(500)). I metodi dei colori accettano un AdaptiveColor, un valore chiaro e uno scuro: AdaptiveColor::rgb(0xffffff, 0x0d1117) cambia con l’aspetto del sistema in modo nativo, senza render. Anche i colori del tema dell’app (theme().primary da nsh_base) sono adattivi (Rust).

In TSX, le proprietà dei colori accettano stringhe di colore CSS:

FormaEsempio
Esadecimale"#09f", "#09fc", "#0099ff", "#0099ffcc"
rgb() / rgba()"rgb(0, 153, 255)", "rgba(0, 0, 0, 0.5)", "rgb(0 0 0 / 10%)"
hsl() / hsla()"hsl(210 100% 50%)"
oklch() / oklab()"oklch(62.3% 0.214 259.8)"
Con nome"white", "black", "transparent", "rebeccapurple", …
currentColor"currentColor": il colore del testo, anche all’interno di un gradiente, di un’ombra, di un drop-shadow() o di un color-mix() ("color-mix(in srgb, currentColor 50%, transparent)"); le classi lo scrivono bg-current, border-current/50, bg-[color-mix(in_srgb,currentColor_50%,transparent)]

Per la modalità scura in TSX, leggi colorScheme() da nsh, un accessor che segue l’aspetto del sistema, oppure usa la variante dark: di Tailwind:

view()
    .flex_1()
    .bg(AdaptiveColor::rgb(0xffffff, 0x0d1117))
    .child(text("Follows the system appearance").text_color(AdaptiveColor::rgb(0x1f2328, 0xe6edf3)))

Per gli stili diversi dai colori, .dark(|s| ..) in Rust applica gli stili nell’aspetto scuro, e dark() legge l’aspetto. Il colore del testo predefinito è nero in entrambi gli aspetti: imposta i colori esplicitamente nelle app che supportano la modalità scura.

#Bordi e angoli

view()
    .border_1()
    .border_color(rgb(0xd0d7de))
    .border_dashed()               // .border_solid() | .border_dashed() | .border_dotted()
    .rounded(px(12.0))
    .rounded_tl(px(24.0))          // per corner, overrides .rounded(..)
    .border_b(px(3.0))             // per side
    .border_b_color(rgb(0x0969da))
  • Larghezze e colori per lato: borderTopWidth, borderRightWidth, …, borderTopColor, …, che mantengono il proprio lato in entrambe le direzioni; e borderStartWidth, borderEndWidth, borderStartColor, borderEndColor, che seguono la direzione del layout (.border_t(..), .border_t_color(..), .border_s(..), .border_e_color(..), … in Rust).
  • Il colore di un lato di cui è impostata la larghezza ma non il colore è il colore del testo, come currentColor del CSS: { borderWidth: 1, color: "#0969da" } disegna un bordo blu.
  • I raggi per angolo: borderTopLeftRadius, borderTopRightRadius, borderBottomRightRadius, borderBottomLeftRadius, e borderTopStartRadius / borderTopEndRadius / borderBottomStartRadius / borderBottomEndRadius (alto inizio, alto fine, basso inizio, basso fine), che seguono la direzione del layout (.rounded_tl(..), …, .rounded_ss(..), .rounded_se(..), .rounded_es(..), .rounded_ee(..) in Rust, e i preset .rounded_lg(), .rounded_full(), …).
  • borderCurve: "continuous" (.border_curve(BorderCurve::Continuous)) disegna angoli la cui curvatura sfuma dolcemente nei bordi, come le icone delle app iOS, su entrambe le piattaforme. Una pillola (borderRadius pari a metà altezza) resta una pillola.
  • overflow: "hidden" (.overflow_hidden()) ritaglia i figli secondo la forma arrotondata. Ciò che viene ritagliato lo è anche nel layout: non raggiunge la dimensione del contenuto di una scroll view contenitore, quindi un box che dispone un figlio più largo di sé e lo riduce con un transform (le trasformazioni non influiscono sul layout) non altera la scroll view.

I raggi maggiori di metà del lato vengono limitati, quindi borderRadius: 9999 (.rounded_full()) crea un cerchio o una pillola.

#Ombre

Le viste proiettano le boxShadow del CSS, e Android aggiunge la propria material elevation:

// CSS box-shadow: several layers, with spread and inset.
view().shadow([
    BoxShadow { offset_y: px(1.0), blur: px(2.0), color: rgba(0x00000014).into(), ..BoxShadow::default() },
    BoxShadow { offset_y: px(8.0), blur: px(24.0), color: rgba(0x0000001f).into(), ..BoxShadow::default() },
])
view().shadow([BoxShadow { offset_y: px(2.0), blur: px(4.0), color: rgba(0x00000026).into(), inset: true, ..BoxShadow::default() }])

// A preset in a color of its own.
view().shadow_lg().shadow_color(rgba(0x0969da66))

// Android's material elevation.
view().elevation(px(6.0)).bg(white()).rounded(px(12.0))

boxShadow segue il CSS: offsetX offsetY blur spread color, inset per le ombre interne. Accetta i propri livelli anche come numeri, la forma adatta ai valori che cambiano, che non costruisce alcuna stringa: boxShadow: [{ x: 0, y: 6, blur: 18, spread: 0, color: "#0000004d", inset: false }] (solo x e y sono obbligatori; un livello senza colore usa il colore del testo). Rust accetta i livelli, .shadow([BoxShadow { .. }]). Le ombre seguono la forma arrotondata della vista, e una transizione le anima (transitionProperty: "boxShadow", .transition_shadow()). I preset di Tailwind sono .shadow_sm(), .shadow_md(), .shadow_lg(), … in Rust, e .shadow_color(..) ricolora i loro livelli. Un’ombra che segue il contenuto di una vista trasparente (ad esempio i glifi di un’icona) è un filter: "drop-shadow(...)" (Filtri); il testo ha textShadow (Testo).

#Gradienti

backgroundImage accetta gradienti CSS, disegnati sopra il colore di sfondo; in Rust, .bg_linear(angle), .bg_radial() e .bg_conic(angle) con gli stop .gradient_from(..), .gradient_via(..) e .gradient_to(..), come bg-linear-* di Tailwind con from-*, via-* e to-*. In TSX, più gradienti sono separati da virgole.

view().h(px(120.0)).rounded(px(16.0)).bg_linear(deg(135.0)).gradient_from(rgb(0x54aeff)).gradient_to(rgb(0x8250df))
view().bg_radial().gradient_from(rgb(0xfff8c5)).gradient_to(rgb(0xfd8c73))
view().bg_conic(deg(0.0)).gradient_from(rgb(0xff0000)).gradient_via(rgb(0x00ff00)).gradient_to(rgb(0x0000ff))
view()
    .bg_linear_to_r()
    .gradient_from(rgb(0x8250df))
    .gradient_from_position(percentage(0.3))
    .gradient_to(rgb(0x54aeff))
    .gradient_to_position(percentage(0.3))

Gli stop di colore sono posizionati con percentuali o lunghezze (px, pt, rem; i numeri semplici sono punti) lungo un gradiente lineare o radiale, e con percentuali o angoli (deg, turn, rad, grad) attorno a uno conico. Uno stop con due posizioni (#8250df 0 12px) è una banda di un solo colore, come lo sono due stop nella stessa posizione. Le lunghezze sono misurate lungo la linea del gradiente alla dimensione attuale della vista, quindi una banda di 12px resta larga 12px quando la vista viene ridimensionata.

I colori si mescolano tra gli stop come in CSS: nello spazio indicato da un gradiente (linear-gradient(in oklab, …); anche srgb, srgb-linear, xyz, oklch con un metodo per la tonalità come longer hue, e hsl), e altrimenti in sRGB quando ogni colore è esadecimale, rgb(), hsl() o con nome, e in Oklab quando uno è oklch(), oklab(), lab(), lch() o color().

Gli elementi di testo non disegnano gradienti su iOS.

#Filtri e fusione

ProprietàValoreRust
filterFiltri CSS della vista e del suo contenuto: brightness(), contrast(), grayscale(), hue-rotate(), invert(), saturate(), sepia(), opacity(), blur(), drop-shadow() (senza colore, nel colore del testo, come in CSS); più filtri separati da spazi.brightness(n), .contrast(n), .grayscale(n), .hue_rotate(angle), .invert(n), .saturate(n), .sepia(n), .blur(px(..))
backdropFilterFiltri di ciò che sta dietro la vista: "blur(10px)" (vetro smerigliato).backdrop_blur(px(10.0)), .backdrop_saturate(n), …
glassEffect"regular" o "clear" per il Liquid Glass di Apple su iOS 26 e macOS 26 e successivi; "none" (il valore predefinito) usa backdropFilter.glass_effect(GlassEffect::Regular)
mixBlendModeCome la vista si fonde con ciò che sta dietro: "multiply", "screen", "overlay", ….mix_blend_mode(BlendMode::Multiply)
image(photo).size(px(120.0)).grayscale(1.0).contrast(1.2)
view()
    .absolute()
    .bottom_0()
    .left_0()
    .right_0()
    .p_3()
    .bg(rgba(0xffffff66))
    .backdrop_blur(px(12.0))

Il supporto varia tra le piattaforme:

iOSAndroid
filter sulle visteopacity, brightness fino a 1 e solo drop-shadowTutti, da Android 12 (API 31); sotto, solo i filtri colore
filter sulle immaginiOgni filtro colore e blurCome per le viste
backdropFilterUn materiale di sfocatura di sistema, che approssima il raggioDa Android 12 (API 31)
mixBlendModeSìDa Android 10 (API 29)

Web, Windows e Linux applicano ogni filtro alle viste, macOS come iOS; GPUI disegna i filtri colore e drop-shadow(). Il filtro di sfondo è una sfocatura su iOS, macOS e Android, ogni filtro sul web e su Windows; Linux e GPUI non ne disegnano alcuno, e GPUI disegna le viste in modalità di fusione normale. Di un filter con più drop-shadow(), iOS, macOS e Android disegnano l’ultimo. mixBlendMode fonde la vista con tutto ciò che è disegnato dietro di essa.

Su iOS, ios={{ backdropMaterial: "regular" }} seleziona il materiale di sistema usato da una sfocatura backdropFilter. I valori sono "ultraThin" (il valore predefinito), "thin", "regular", "thick" e "chrome". Abbina la prop a un raggio di sfocatura positivo per mostrare il materiale. Rust usa .backdrop_material(BackdropMaterial::Regular) insieme a .backdrop_blur(px(30.0)). Il raggio approssima l’intensità del materiale: NSH applica una frazione del materiale scelto, raggiungendo la piena intensità a 30 punti. La variante chiara o scura segue il colore di sfondo della vista. Queste scelte corrispondono ai materiali di sfocatura di sistema di Apple.

view().backdrop_blur(px(30.0)).backdrop_material(BackdropMaterial::Regular)

glassEffect disegna il Liquid Glass dietro il contenuto di una vista su iOS 26 e macOS 26 e successivi. "regular" usa il materiale standard; "clear" assorbe meno colore dallo sfondo. Abbinalo a backdropFilter per la sfocatura mostrata nelle versioni precedenti di iOS e su altre piattaforme. Il vetro segue la dimensione e gli angoli arrotondati della vista.

<View style={{ height: 80, borderRadius: 16, backdropFilter: "blur(12px)", glassEffect: "regular" }} />

#Outline, opacità e visibilità

  • outlineWidth, outlineColor, outlineStyle, outlineOffset (.outline(..), .outline_color(..), .outline_dashed(), .outline_offset(..)): una linea esterna al bordo che non occupa spazio (gli offset negativi disegnano all’interno). outlineStyle: "none" (o "hidden", .outline_none()) nasconde l’outline qualunque sia la sua larghezza, come in CSS.
  • opacity (.opacity(n)): da 0 a 1, applicata alla vista e ai suoi figli.
  • visibility: "hidden" (.invisible() in Rust): la vista non viene disegnata ma mantiene il suo spazio, e gli screen reader la saltano insieme al suo sottoalbero, come in un browser. display: "none" (.hidden()) la rimuove dal layout.
  • zIndex (.z_index(n)): l’ordine tra i fratelli, anche l’ordine di sovrapposizione nativo (stabile per valori uguali).
  • cursor (.cursor(..), .cursor_pointer()): la forma del puntatore sulla vista, per puntatori e desktop.

#Trasformazioni

Le trasformazioni sono quelle di CSS. In TSX, translate, rotate e scale sono proprietà a sé stanti e transform accetta la stringa di funzioni di CSS o un elenco di passi; si compongono nell’ordine di CSS, translate, poi rotate, poi scale, poi transform, qualunque sia l’ordine delle chiavi. In Rust, le trasformazioni sono metodi (.translate_x(..), .rotate(..), .scale(..), …) composti nello stesso ordine:

view().translate_x(px(20.0)).rotate(deg(-8.0)).scale(0.9)
view().perspective(px(600.0)).child(view().rotate_y(deg(35.0)).backface_hidden())
view().origin_top_left().rotate(deg(30.0))
ProprietàValore
translate"10px 20px", "-50% 0" (percentuali della dimensione della view stessa), un numero (punti lungo x) o [x, y] con un numero, una stringa o un valore animato per asse
rotate"45deg", "0.25turn", attorno a un asse "x 90deg", un numero (gradi) o un valore animato (gradi)
scale1.5, "150%", "1 2" (x e y), un valore animato (entrambi gli assi) o [x, y]
transformFunzioni di CSS: translate(), translateX(), translateY(), scale(), scaleX(), scaleY(), rotate() / rotateZ(), rotateX(), rotateY(), skew(), skewX(), skewY(), perspective(); lunghezze in px (le traslazioni anche %), angoli in deg, rad o turn; "none"; oppure un elenco di passi ([{ perspective: 800 }, { rotateY: angle }]) le cui quantità possono essere valori animati

Ogni proprietà è a sé stante, come per le classi: il scale di uno stile sostituisce una classe scale-95 e mantiene la sua rotate-3. I valori animati muovono translate, rotate e scale in modo nativo; per ciò che la stringa di CSS non può contenere con una quantità animata, come una rotazione 3D, passa a transform un elenco di passi: translateX, translateY, scale, scaleX, scaleY, rotate (rotateZ), rotateX, rotateY, skewX, skewY e perspective (Animation). Il transform di uno stile è l’una o l’altra forma, quindi una prospettiva prima di passi animati è un passo dell’elenco.

Il .perspective(..) di Rust è la proprietà perspective di CSS, come perspective-* di Tailwind: dà profondità ai figli della view, mentre la funzione perspective() di un transform si applica alla trasformazione della view stessa. transformOrigin accetta parole chiave, percentuali e lunghezze ("top left", "50% 0", [10, 20]; .origin_top_left(), .transform_origin(..) in Rust); il valore predefinito è il centro. Le trasformazioni non influenzano il layout: i fratelli vengono posizionati come se la view non fosse trasformata.

Android non supporta le trasformazioni skew.

#Transizioni

Una view anima le modifiche del proprio stile quando indichi cosa animare. L’animazione viene eseguita in modo nativo; nessuna parte del tuo codice viene eseguita a ogni frame.

let open = signal(false);

view()
    .on_press(move || open.update(|o| *o = !*o))
    .w(px(150.0))
    .h(px(72.0))
    .rounded(move || px(if open.get() { 36.0 } else { 12.0 }))
    .bg(move || if open.get() { rgb(0x8250df) } else { rgb(0x54aeff) })
    .translate_x(move || px(if open.get() { 120.0 } else { 0.0 }))
    .transition_all()
    .duration(300.0)
    .ease_in_out()
ProprietàValoreRust
transitionPropertyCosa viene animato: "all", oppure nomi separati da virgole: colori ("colors", "backgroundColor", "borderColor", …), "opacity", "transform" (ogni proprietà di trasformazione), "boxShadow", "borderRadius" / "borderWidth".transition(), .transition_all(), .transition_colors(), .transition_opacity(), .transition_transform(), .transition_shadow()
transitionDuration300, "300ms", "0.3s".duration(300.0)
transitionDelayCome la durata.delay(100.0)
transitionTimingFunction"linear", "ease", "ease-in", "ease-out", "ease-in-out", "cubic-bezier(x1, y1, x2, y2)".ease_linear(), .ease_in(), .ease_out(), .ease_in_out(), .ease_bezier(x1, y1, x2, y2)

Dimensioni e posizioni fanno parte del layout, non dello stile, e non hanno transizioni; anima le modifiche di layout con LayoutAnimation oppure guida i valori con animazioni native (Animation).

#Animazioni keyframe

animationName esegue in modo nativo una delle animazioni keyframe integrate di Tailwind: "spin", "ping", "pulse" o "bounce", con animationDuration, animationDelay, animationTimingFunction e animationIterationCount (un numero o "infinite"), in qualsiasi ordine. In Rust, .animate_spin(), .animate_ping(), .animate_pulse() e .animate_bounce() le eseguono con i tempi di Tailwind, e .animation(KeyframeAnimation::new(..)) con i tuoi:

view()
    .size(px(24.0))
    .rounded(px(4.0))
    .bg(rgb(0x0969da))
    .animation(KeyframeAnimation::new(Keyframes::Spin).duration(1000.0).easing([0.0, 0.0, 1.0, 1.0]))

#Feedback alla pressione

Il feedback alla pressione è l’insieme degli stili dell’app per lo stato di pressione, applicati in modo nativo senza un viaggio di andata e ritorno verso il tuo codice; gli stati hover, focus e disabilitato accettano stili allo stesso modo (.hover(..), .focus(..), .disabled_style(..) in Rust; hoverStyle, focusStyle e disabledStyle, oppure hover:, focus:, disabled:, in TSX):

RustTSXEffetto durante la pressione
.active(|s| s.opacity(0.6))activeStyle={{ opacity: 0.6 }}La view sfuma fino a questa opacità
.active(|s| s.bg(rgb(0xeaeef2)))activeStyle={{ backgroundColor: "#eaeef2" }}Lo sfondo assume questo colore
.ripple(Ripple { color: Some(rgba(0x00000022).into()), ..Ripple::default() })android={{ ripple: { color: "#0002" } }}Il ripple material di Android
.active(|s| s.bg(ZINC.shade(100)).scale(0.95))class="active:bg-zinc-100 active:scale-95"Qualsiasi stile

Vedi Touch and gestures.

#Estensioni

Queste chiavi e classi gestiscono ciò che le piattaforme native offrono oltre CSS:

ChiaveClasseCosa fa
placeholderColorplaceholder:text-*Il colore del segnaposto di un <Input>
selectionColorselection:bg-*L’evidenziazione della selezione di un <Input> o di un <Text> selezionabile
tintColortint-*Ricolora i pixel opachi di un <Image> (immagini template, icone)
borderCurve"continuous": angoli che si raccordano con i bordi in modo graduale, come quelli del sistema (gli angoli continui di Apple)
glassEffectIl Liquid Glass di Apple dietro la view su iOS 26 e macOS 26, backdropFilter altrove
writingDirectionLa direzione di base di un testo, "auto" (il valore predefinito: dedotta dal contenuto, come fanno le view di testo delle piattaforme), "ltr" o "rtl"; direction imposta quella del layout
textAlignVerticalDove si colloca il testo più corto del suo riquadro <Text>: "top" (il valore predefinito), "center" o "bottom"
textAlign: "auto"Il lato naturale del testo, in base alla sua direzione (il valore predefinito)
selectedStyleselected:Lo stile mentre accessibilityState.selected è true (un Button selezionato, una tab)
ios:, android:, web:Classi per una sola piattaforma (ios:pt-2)

I valori che cambiano accettano anche numeri dove CSS accetta una stringa: transform come elenco di passi, translate e scale come [x, y], boxShadow e textShadow come livelli.

#Correlati

  • Tailwind: gli stessi stili come classi, con varianti per la modalità scura, gli stati di pressione e i breakpoint, e i loro equivalenti tipizzati.
  • Layout e Text.
  • Style reference: ogni proprietà di stile e il suo tipo.