Interatividade e navegação
Alguns documentos não são lineares: você quer abrir um detalhe só se alguém perguntar, comparar dois cenários com um clique ou pular para uma seção e voltar. A Ribalta faz isso sem JavaScript, com uma camada pequena de estado:
| Recurso | Para quê |
|---|---|
@state | Uma variável que muda enquanto o documento é mostrado. |
click="…" | Ações ao clicar: mudar o estado, ir para outra página, voltar. |
if | Mostrar ou esconder conteúdo conforme o estado. |
.estilo=condição | Aplicar um estilo só quando a condição é verdadeira. |
$nome no texto | Mostrar o valor atual de uma variável. |
@state x = [] | Uma lista de textos que muda enquanto o documento é mostrado, como um inventário. |
random, pick | Sortear um número ou um item dentro de uma ação. |
$elapsed, timeout | Contar o tempo na página e agir quando ele acaba. |
:: input | Um campo de texto para quem assiste digitar. |
key=… | Uma tecla que aciona o clique de um elemento. |
:: sound, play | Sons e efeitos sonoros. |
@persist | Guardar o progresso no navegador. |
@extern | Um valor que vem de quem mostra o documento, como o placar de um jogo. |
--- {hidden} | Páginas fora do fluxo normal, alcançadas só por link ou go. |
--- {on-enter="…"} | Mudar o estado sempre que uma página é aberta. |
--- {nav=off}, @mode game | Páginas que só se deixam pelas opções, para jogos e escolhas. |
Estado com @state
Seção intitulada “Estado com @state”Declare a variável e o valor inicial no início do documento:
@state detalhes = false@state plano = pro@state cliques = 0O valor inicial pode ser verdadeiro/falso, número ou texto. Diferente de @let, o valor de um @state muda enquanto o documento é mostrado: ao clicar em um elemento, ao navegar ou ao entrar em uma página.
Cliques
Seção intitulada “Cliques”O atributo click recebe uma ou mais ações, separadas por ;:
@state detalhes = false
# Por que o custo cai?[Mostrar detalhes]{click="toggle $detalhes" .accent}::: if $detalhes::: box {.card enter=up}A renderização acontece no navegador, então não há servidores de exportação.::::::| Ação | Efeito |
|---|---|
$x = valor | Muda o valor. O tipo precisa ser o mesmo do @state (R602). |
toggle $x | Inverte verdadeiro/falso. |
add $lista valor / remove $lista valor | Põe ou tira um texto de uma lista. Veja Listas no estado. |
clear $lista | Esvazia a lista. |
go #id | Vai para a página com esse id. |
go back | Volta para onde você estava antes do último go ou link. |
go next / go prev | Avança ou volta uma etapa, como as setas. |
click funciona em qualquer bloco (::: box {click="…"}) e em trechos de texto ([texto]{click="…"}). Clicar num elemento interativo não avança a página. Só variáveis de @state podem mudar (R605).
Conteúdo que depende do estado
Seção intitulada “Conteúdo que depende do estado”Use ::: if, ::: else e o atributo if exatamente como em Variáveis, dados e repetição. A diferença é que, com @state, todas as alternativas vão para o documento e a escolha acontece na hora:
@state cenario = otimista
## Receita em 2027[Otimista]{click="$cenario = otimista"} · [Conservador]{click="$cenario = conservador"}::: if $cenario == otimista# R$ 20 milhões {color=green}:::::: else# R$ 14 milhões {color=coral}:::Um elemento escondido por if sai do layout e o resto da página se reorganiza. Já um elemento com step reserva o espaço até aparecer.
Variantes: estilo conforme o estado
Seção intitulada “Variantes: estilo conforme o estado”.estilo=condição aplica o @style só quando a condição é verdadeira. É o jeito de destacar o item escolhido:
@state plano = pro@style escolhido {border="4 accent" shadow=lg}@style apagado {opacity=0.45}
::: grid {cols=3 gap=lg}::: box {.card .escolhido="$plano == basic" .apagado="$plano != basic" click="$plano = basic"}### Básico:::::: box {.card .escolhido="$plano == pro" .apagado="$plano != pro" click="$plano = pro"}### Pro:::::: box {.card .escolhido="$plano == team" .apagado="$plano != team" click="$plano = team"}### Equipes::::::Atributos visuais como color, size e w só aceitam valores fixos. Usar @state neles é erro (R604): use uma variante. Assim o compilador valida todas as cores e medidas antes de mostrar o documento.
Ao passar o mouse
Seção intitulada “Ao passar o mouse”.estilo=hover aplica o estilo enquanto o ponteiro do mouse está sobre o elemento. É um bom sinal de que algo é clicável:
@style realce {border="3 accent" shadow=lg}
::: grid {cols=2 gap=lg}::: box {.card .realce=hover click="go next"}### Seguir pela floresta:::::: box {.card .realce=hover click="go next"}### Voltar para a vila::::::- Funciona em blocos, itens de lista e trechos de texto, e pode ser combinado com outras variantes. As mudanças de cor, fundo, borda, sombra e opacidade são animadas.
- Em telas de toque não existe “passar o mouse”, então o estilo não aparece por lá: não use
hoverpara mostrar informação importante. - A condição é exatamente
hover; para combinar com o estado, use duas variantes, como{.realce=hover .escolhido="$plano == pro"}.
Valores no texto
Seção intitulada “Valores no texto”$nome e $( … ) mostram o valor atual:
@state anual = false
## Plano ProR$ $($anual ? 490 : 49) {size=4xl weight=black}$($anual ? "por ano" : "por mês") {.muted}[Cobrança anual: **$($anual ? "ligada" : "desligada")**]{click="toggle $anual"}Listas no estado
Seção intitulada “Listas no estado”Um @state também pode guardar uma lista de textos: os finais que alguém já descobriu, os itens de uma mochila, as perguntas respondidas. A lista não repete itens e mantém a ordem em que eles entraram.
@state mochila = [mapa]
# A porta trancadaVocê carrega: $mochila ($(len($mochila)) itens)
A porta abre. {if="chave in $mochila"}
[Pegar a chave]{click="add $mochila chave"} · [Largar o mapa]{click="remove $mochila mapa"} · [Esvaziar]{click="clear $mochila"}| Recurso | Efeito |
|---|---|
@state x = [], @state x = [a, b] | Declara a lista, vazia ou com itens iniciais. Os itens são textos. |
add $x valor | Põe o valor no fim da lista, se ele ainda não estiver nela. |
remove $x valor | Tira o valor da lista. |
clear $x | Esvazia a lista. |
valor in $x | Verdadeiro se o valor está na lista. Use not (valor in $x) para o contrário. |
len($x) | Quantos itens a lista tem. |
$x no texto | Os itens separados por vírgula. |
As ações de lista funcionam em click e em on-enter. Com elas, um jogo pode contar os finais descobertos sem uma variável para cada final:
@state finais = []
# Início
--- {#vitoria on-enter="add $finais vitoria"}# Você venceuFinais descobertos: $(len($finais)) de 3intambém funciona com uma lista fixa:$rota in [mar, vila]é o mesmo que$rota == mar or $rota == vila.- Listas do estado não se comparam com
==; useinelen(R602). Elas também não mudam com=: useadd,removeeclear. - A Ribalta avisa quando
valor in $xprocura um texto que nenhuma ação adiciona, como um erro de digitação (R608). ::: for item in $xtambém percorre listas do estado: o conteúdo é repetido enquanto o documento é mostrado e acompanha cadaadderemove. Dentro dele, ações não usam$item(R620).
Sorteios
Seção intitulada “Sorteios”random(menor, maior) sorteia um número inteiro entre os dois valores, incluindo os dois. pick(lista) sorteia um item de uma lista, fixa ou do estado. Os dois só funcionam dentro de ações (click e on-enter), e o resultado vai para um @state:
@state dado = 0@state jogada = nenhuma
# Pedra, papel e tesouraDado: $dado · Jogada: $jogada
[Rolar o dado]{click="$dado = random(1, 6)"} · [Jogar]{click="$jogada = pick([pedra, papel, tesoura])"}- Cada chamada faz um sorteio novo:
$dado = random(1, 6) + random(1, 6)soma dois dados. - Com uma lista do estado, dá para tirar cartas sem repetir:
click="$carta = pick($baralho); remove $baralho $carta". - Fora de uma ação, como em
$(random(1, 6))no texto ou numif, o número mudaria a cada vez que a tela é desenhada, então a Ribalta recusa (R613). Guarde o sorteio num@statee mostre a variável.
$elapsed são os segundos desde que a página abriu: 0, 1, 2… Ele volta a zero sempre que você entra na página. Com ele, dá para mostrar uma contagem regressiva e revelar algo depois de um tempo. timeout e on-timeout no separador rodam ações quando o tempo acaba, como um clique automático:
@state pontos = 0
--- {timeout=30 on-timeout="go #esgotado"}# Qual é a capital do Brasil?Restam $(30 - $elapsed) segundos
Dica: começa com B {if="$elapsed >= 15"}
[Brasília]{click="$pontos = $pontos + 1; go #acertou"} · [Rio de Janeiro]{click="go #esgotado"}
--- {#acertou hidden}# Acertou!
--- {#esgotado hidden}# Tempo esgotadoon-timeoutaceita as mesmas ações declick, inclusivego.--- {timeout=10 on-timeout="go next"}avança sozinho, bom para um quiosque.- O relógio só corre enquanto o documento é mostrado de forma interativa. Numa visualização estática, como uma imagem ou um PDF,
$elapsedfica em 0. - Voltar a uma página com
go backrecomeça a contagem.
Entrada de texto
Seção intitulada “Entrada de texto”:: input {bind=$nome} mostra um campo de texto ligado a um @state. Com ele, a história chama a pessoa pelo nome, um quiz recebe respostas escritas e uma porta pede senha:
@state senha = ""
# A porta pede uma senha:: input {bind=$senha placeholder="Senha" on-submit="go #dentro"}
A porta range… {if="$senha == abracadabra"}
--- {#dentro hidden}# Você entrouon-submitroda ações quando a pessoa aperta Enter, inclusivego. Semon-submit, o Enter no campo aciona o elemento da página comkey=enter, como o botão de enviar de um formulário.- O valor muda a cada tecla. Para aceitar variações de uma resposta, use uma lista:
{if="$resposta in [Brasília, brasília, brasilia]"}. - Digitar no campo não avança a página, e as setas movem o cursor dentro dele.
- Detalhes em Blocos › input.
Teclado
Seção intitulada “Teclado”key dá a um elemento clicável uma tecla de atalho. Apertar a tecla roda as mesmas ações do click, como num jogo de mover pelo mapa:
@state x = 0@state y = 0
--- {nav=off}# Posição: $x, $y[↑]{click="$y = $y - 1" key=up} [↓]{click="$y = $y + 1" key=down} [←]{click="$x = $x - 1" key=left} [→]{click="$x = $x + 1" key=right}
[Recomeçar]{click="$x = 0; $y = 0" key=r}- Teclas aceitas: uma letra, um número,
up,down,left,right(as setas),EntereSpace. - Uma tecla usada num elemento deixa de navegar naquela página: com
key=right, a seta direita move o personagem em vez de avançar. Combine comnav=offquando a página inteira for um jogo. - Só valem os elementos que estão aparecendo: um
iffalso ou uma etapa ainda não revelada desligam a tecla. - Todo elemento clicável também pode ser alcançado com Tab e acionado com Enter ou espaço, para quem navega sem mouse. Uma variante
.estilo=hovertambém aparece quando o elemento recebe o foco pelo teclado.
:: sound declara um som na página, e as ações play e stop o controlam. Os sons param quando você sai da página:
--- {on-enter="play chuva"}:: sound https://exemplo.com/chuva.mp3 {name=chuva loop volume=40%}:: sound https://exemplo.com/porta.mp3 {name=porta}# A porta[Abrir a porta]{click="play porta; go next"}playestopfuncionam emclick,on-enter,on-timeouteon-submit.playrecomeça o som do início.autoplaytoca o som ao entrar na página, sem precisar de ação.- O som só toca quando o documento é mostrado de forma interativa. Visualizações estáticas, como imagens e PDF, ficam mudas, e o programa que mostra o documento pode silenciar telas extras para o som não tocar duas vezes.
- Os navegadores só tocam som depois de algum clique ou tecla; na primeira página mostrada, o
autoplaypode esperar a primeira interação. - Use endereços que comecem com
https://.
Progresso salvo
Seção intitulada “Progresso salvo”@persist lista os @state que ficam guardados no navegador de quem assiste. Ao fechar a página e voltar, o progresso continua de onde parou:
@state finais = []@state moedas = 0@persist finais moedas- Só valem
@statedo início do documento (R617). As outras variáveis começam do valor inicial, como sempre. - O progresso fica no navegador de cada pessoa, separado para cada endereço.
- Para recomeçar do zero, use as próprias ações:
click="clear $finais; $moedas = 0".
Nota e conclusão em LMS (SCORM)
Seção intitulada “Nota e conclusão em LMS (SCORM)”Treinamentos e quizzes podem rodar dentro de um LMS (Moodle, Canvas, TalentLMS…) e mandar a nota e a conclusão para ele. Diga qual @state é a nota e em quais páginas o aluno conclui:
@state pontos = 0@scorm score=$pontos complete=#fim pass=70
--- {#inicio}# Qual senha é mais forte?[Abacate-Lua-Trem-42]{click="$pontos = $pontos + 100; go #fim"}[123456]{click="go #fim"}
--- {#fim hidden}# Você fez $pontos pontosO programa que mostra o documento gera o pacote SCORM, um .zip que você envia ao LMS.
| Parâmetro | O que faz |
|---|---|
score=$variavel | @state numérico enviado como nota, sempre que muda. |
complete=#pagina | Página que conclui o curso ao ser alcançada. Vários: complete=#venceu,#perdeu. |
max=100 | Nota máxima (padrão 100). A nota enviada fica entre 0 e max. |
pass=70 | Nota mínima para aprovar. Com ela, a conclusão vira “aprovado” ou “reprovado”; sem ela, “concluído”. |
- A comunicação com o LMS usa SCORM 1.2, aceito por praticamente todos os LMS.
- O LMS guarda a sessão: se o aluno sair no meio, volta para a mesma página com o mesmo estado.
- Sem
@scorm, o pacote só avisa a conclusão quando o aluno chega à última página. - Fora de um LMS,
@scormnão faz nada.
Valores de fora com @extern
Seção intitulada “Valores de fora com @extern”Às vezes o documento mostra algo que ele mesmo não decide: o placar de um jogo que roda num servidor, a pergunta da rodada, a cotação do dia. @extern declara um valor assim. Quem mostra o documento, como um app que embute a Ribalta, define o valor e pode trocá-lo a qualquer momento, e as páginas se redesenham na hora.
@extern pergunta = "Qual é a capital do Brasil?"@extern rodada = 3@extern revelada = false
Pergunta $rodada {.kicker}# $perguntaBrasília {if="$revelada" color=green}- O valor depois do
=é o exemplo: define o tipo (verdadeiro/falso, número, texto ou lista de textos) e é o que aparece quando ninguém fornece o valor, como numa prévia ou num PDF. - Use
$nome,if, variantes e expressões como com@state. - Só quem mostra o documento muda um
@extern: ações,:: input,@persiste oscorede@scormnão aceitam (R619). Para um valor que o próprio documento muda, use@state.
Listas de registros
Seção intitulada “Listas de registros”Um @extern também pode ser uma lista de registros, como o placar de um jogo. O exemplo é uma tabela @data, que define os campos e o que aparece sem host; ::: for repete o conteúdo para cada linha que chegar:
@data exemplo| nome | pontos || --- | --- || Ana | 300 || Bruno | 200 |@end@extern placar = $exemplo
## Placar::: for jogador, i in $placar$i. **$jogador.nome**: $jogador.pontos pontos:::- A tabela de exemplo precisa de pelo menos uma linha: ela dá o tipo de cada campo.
- Quem mostra o documento pode enviar mais ou menos linhas; o
foracompanha. - Dentro do
for, use$jogador.campono texto, emife em variantes. Ações não usam o item (R620), e atributos visuais comosizecontinuam só com valores fixos. len($placar)dá o número de linhas.
Navegação
Seção intitulada “Navegação”Links e go
Seção intitulada “Links e go”Um link com #id vai para a página com esse id. click="go #id" faz o mesmo e pode ser combinado com outras ações; elas são executadas em ordem, então a página de destino já aparece com o novo estado:
--- {#precos}## Escolha um plano[Plano Pro →]{click="$plano = pro; go #detalhe"}[Plano Equipes →]{click="$plano = team; go #detalhe"}Desvios com hidden
Seção intitulada “Desvios com hidden”Uma página com hidden fica fora do fluxo normal: as setas pulam essa página e ela só é alcançada por link ou go. Quando você avança depois da última etapa de uma página oculta, volta para onde estava.
@state plano = pro
# Escolha um plano[Ver o Pro](#detalhe)--- {#detalhe hidden}::: if $plano == pro## Pro: até 25 usuários:::[← Voltar]{click="go back"}Uma página oculta que nenhum link ou go alcança gera um aviso (R702).
Mudando a próxima página
Seção intitulada “Mudando a próxima página”--- {next=#id} troca a página seguinte quando você avança, para criar caminhos alternativos sem duplicar o documento.
| Tipo | Como | O que acontece |
|---|---|---|
| Normal | setas, espaço, clique na página | Próxima etapa ou próxima página visível. |
| Salto | [x](#id), click="go #id" | Vai à página e guarda a posição de origem. |
| Com estado | click="$x = v; go #id" | Muda o estado e depois vai à página. |
| Volta | click="go back" | Retorna à posição guardada pelo último salto. |
| Condicional | dois botões com if, ou next=#id | A condição fica no elemento, não na ação. |
Estado dentro de componentes
Seção intitulada “Estado dentro de componentes”Um componente lê e altera direto os @state declarados no início do documento: $pontos no texto, if=$aberto e click="$pontos = $pontos + 1" funcionam dentro do @component como em qualquer página.
Para o mesmo componente servir a variáveis diferentes, passe o @state como parâmetro (estado=$q1): dentro dele, $estado = feito muda o $q1 original. Destinos de go também aceitam parâmetros, como go #pergunta-$n. Assim um único componente atende vários itens:
@state q1 = pendente@state q2 = pendente@component item {n estado}[Item $n: $estado]{click="$estado = feito"}@end
:: item {n=1 estado=$q1}:: item {n=2 estado=$q2}O @state de uma página (veja abaixo), o @let de uma página e as variáveis de ::: for só existem onde o componente é usado, então o componente não os enxerga: passe-os como parâmetro (R410).
Estado de uma página
Seção intitulada “Estado de uma página”Um @state no topo de uma página, logo depois do ---, só existe nessa página. Ele volta ao valor inicial sempre que você entra na página, e go back restaura o valor que estava:
# Início--- {#demo}@state aberto = false## Demonstração[Abrir]{click="toggle $aberto"}Conteúdo da demonstração {if=$aberto}Ação ao entrar na página
Seção intitulada “Ação ao entrar na página”on-enter no separador roda ações sempre que a página passa a ser a atual: ao começar a mostrar o documento, ao avançar ou voltar com as setas, por go, link ou next. Ele não roda de novo quando você volta com go back, porque isso é retornar a um ponto em que a página já foi aberta.
@state visitas = 0@state viu_o_final = false
# InícioEsta é a página de abertura.
--- {on-enter="$visitas = $visitas + 1"}# MeioVocê já passou por aqui $visitas vez(es).
--- {on-enter="$viu_o_final = true"}# Fim- As ações são as mesmas de
clickque mudam o estado:$x = valoretoggle $x, separadas por;. Ir para outra página (go,go back,go next,go prev) não é permitido (R612); para escolher a página seguinte, usenext=#id. - O estado da própria página volta ao valor inicial antes das ações, então
on-entertambém pode ajustá-lo. on-enterfunciona dentro de um@style, o que permite reaproveitar a mesma ação em várias páginas. As ações do estilo rodam antes das escritas no separador:
@state finais = 0@style final {on-enter="$finais = $finais + 1"}
--- {#vitoria .final}# Você venceuFinais descobertos: $finaisPáginas sem avanço e modo jogo
Seção intitulada “Páginas sem avanço e modo jogo”Num jogo ou numa história de escolhas, quem decide a próxima página é a opção clicada, não a seta. Com nav=off no separador, as setas, o espaço, o clique na página e o deslizar não saem dela:
@state rota = nenhuma
--- {nav=off}# A encruzilhada- Uma pista aparece {step=1}[Ir para o mar]{click="$rota = mar; go #mar"}[Ir para a vila]{click="$rota = vila; go #vila"}
--- {#mar hidden}# O mar[Voltar]{click="go back"}
--- {#vila hidden}# A vila[Voltar]{click="go back"}- As etapas da página continuam sendo reveladas e escondidas pelas setas.
- As ações escritas na página continuam funcionando, inclusive
go nextego prev. - Numa página
hiddenaberta porgo, a seta também não volta sozinha para onde você estava. Para isso, ofereça um[Voltar]{click="go back"}.
@mode game no início do documento (ou no tema base) faz isso em todas as páginas. Ele também avisa ao programa que mostra o documento que se trata de um jogo, para ele esconder o que entregaria o final antes da hora, como o contador de páginas ou um atalho para a última página. O modo padrão é @mode default. Uma página pode voltar ao normal com nav=on:
@mode game
# A Última Luz[Começar]{click="go #capitulo-1"}
--- {#capitulo-1}# Capítulo 1
--- {nav=on}# CréditosEtapas com $step
Seção intitulada “Etapas com $step”$step é a etapa atual da página. Com ela, uma revelação pode substituir em vez de só acumular, como apagar os itens anteriores:
@let etapas = [Importar, "Ajustar o tema", Compartilhar]@style apagado {opacity=0.35}
## Como começar::: for t, i in $etapas1. $t {step=$i .apagado="$step > $i"}:::O número de etapas da página considera step, ::: steps e os números comparados com $step. Para fixar outro valor, use --- {steps=4}.
Animações
Seção intitulada “Animações”| Atributo | Onde | Valores | Efeito |
|---|---|---|---|
enter | Qualquer bloco ou trecho | fade, up, down, left, right, zoom, none | Como o elemento aparece numa etapa ou quando um if passa a ser verdadeiro. O padrão das etapas é up. |
transition | Separador --- | fade, slide, zoom, none | Transição ao entrar na página. |
@transition | Preâmbulo | Os mesmos de transition | Transição padrão de todas as páginas. |
Como enter é um atributo comum, ele pode morar num @style (@style surge {enter=zoom}), deixando a animação fora da estrutura da página. Mudanças de variante (cor, fundo, opacidade, sombra) são animadas automaticamente.
Visualizações estáticas
Seção intitulada “Visualizações estáticas”Uma visualização estática do documento, como uma imagem ou um PDF, usa o estado inicial e mostra todas as etapas.
Erros comuns
Seção intitulada “Erros comuns”| Código | Quando acontece |
|---|---|
| R604 | @state usado num atributo visual ou num @let. |
| R605 | click tentando mudar um @let, um dado ou um parâmetro. |
| R608 | Comparação com um valor que o estado nunca recebe, como $plano == prro. |
| R611 | Ação de click mal escrita. |
| R612 | on-enter tentando mudar de página. |
| R613 | random ou pick fora de uma ação. |
| R614 | on-timeout sem timeout. |
| R116 | :: input sem bind. |
| R210 | key sem click. |
| R615 | play ou stop com um som que não está na página. |
| R617 | @persist com algo que não é um @state. |
| R618 | @scorm com parâmetro desconhecido, nota que não é um @state numérico ou número inválido. |
| R534 | @mode com um valor diferente de default ou game. |
| R701 | go #id, link ou next para uma página que não existe. |
| R702 | Página hidden que ninguém alcança. |