# Snora

[![snora](https://img.shields.io/crates/v/snora?label=snora)](https://crates.io/crates/snora)
[![snora Docs](https://docs.rs/snora/badge.svg?version=latest)](https://docs.rs/snora)
[![snora Deps Status](https://deps.rs/crate/snora/latest/status.svg)](https://deps.rs/crate/snora)    
[![snora-core](https://img.shields.io/crates/v/snora-core?label=snora-core)](https://crates.io/crates/snora-core)
[![snora-core Docs](https://docs.rs/snora-core/badge.svg?version=latest)](https://docs.rs/snora-core)
[![snora-core Deps Status](https://deps.rs/crate/snora-core/latest/status.svg)](https://deps.rs/crate/snora-core)    
[![License](https://img.shields.io/github/license/nabbisen/snora)](https://github.com/nabbisen/snora/blob/main/LICENSE)
[![Docs](https://github.com/nabbisen/snora/actions/workflows/docs.yaml/badge.svg)](https://github.com/nabbisen/snora/actions/workflows/docs.yaml)

**An iced GUI framework that gets out of the way of your application.**

## Overview

Snora gives an iced application a small, opinionated **skeleton**
(header, sidebar, body, footer) plus the overlay surfaces it almost
certainly needs (dialog, bottom sheet, context menu, toasts) — and
then steps back. Every slot accepts any `iced::Element`, so your UI
code stays your UI code.

## When to use it

snora is a good fit when you are building:

- a **local-first desktop app** that runs heavy work alongside an
  interactive UI (AI inference, dataset converters, file processors);
- an app that needs **accessibility correct from day one** — RTL
  layout, theme-aware colors, logical edges baked into the API;
- a **standard desktop chrome** (header / sidebar / body / footer)
  with a few overlays.

snora is *not* the right tool for games, real-time visualization,
or web-first applications. See
[docs/getting-started/05-when-to-use.md](docs/src/getting-started/05-when-to-use.md)
for fuller fit guidance.

## Quick start

```toml
[dependencies]
iced  = { version = "0.14", features = ["tokio"] }
snora = "0.24"
```

```rust
use iced::{Element, widget::text};
use snora::{AppLayout, render};

#[derive(Debug, Clone)]
enum Message {}

#[derive(Default)]
struct App;

impl App {
    fn update(&mut self, _msg: Message) {}
    fn view(&self) -> Element<'_, Message> {
        let body: Element<'_, Message> = text("Hello, snora!").into();
        render(AppLayout::new(body))
    }
}

fn main() -> iced::Result {
    iced::application(App::default, App::update, App::view)
        .title(|_: &App| String::from("Hello"))
        .run()
}
```

That's the whole program. Adding a header, sidebar, and footer is
three more chained calls — see
[docs/getting-started](docs/src/getting-started/) for the step-by-step
walk-through, or jump straight to the
[**workbench example**](https://github.com/nabbisen/snora/tree/main/examples/workbench)
to see every surface working together (header, sidebar, menus, dialog,
sheet, toasts, tabs, breadcrumb, and live LTR↔RTL toggle). Single-surface
demos are in the
[examples directory](https://github.com/nabbisen/snora/tree/main/examples).

## Features

- **Skeleton + injected slots.** `AppLayout::new(body).header(h).side_bar(s).footer(f)`.
  Each slot is any `iced::Element`. No trait to implement, no
  dispatcher enum to write.
- **Framework-managed toasts.** `Vec<Toast<Message>>` on your state,
  two one-liners (`subscription` + `sweep_expired`) for lifetime,
  intent → theme color, six anchor positions including RTL-aware ones.
- **One close sink per channel.** `on_close_modals` for dialogs and
  sheets, `on_close_menus` for header / context menus. Wired once.
- **Vocabulary instead of magic numbers.** `SheetSize::Half`,
  `SheetEdge::Start`, `ToastPosition::TopEnd`,
  `LayoutDirection::Rtl`, `Edge::Start` — explicit choices, not
  hardcoded constants.
- **Navigation widgets out of the box.** `app_header`, `app_side_bar`,
  `app_tab_bar`, `app_breadcrumb`, plus dropdown menus and context
  menus — direction-aware, theme-aware, and entirely opt-in.
- **Three crates, one umbrella.** `snora-core` is the iced-free
  vocabulary, `snora-widgets` is the optional prefab UI parts, and
  `snora` is the engine — applications only depend on `snora`.

## Design notes

- *Accessible by Default and by Design.* Layout is described in
  logical edges (`Edge::Start` / `Edge::End`); LTR ↔ RTL is one
  setter on `AppLayout`.
- *No silent drops.* If you populate an overlay but leave its close
  sink `None`, the overlay still renders — snora omits only the
  click-outside backdrop, never the content.
- *Skeleton first, optional design defaults.* Snora positions and stacks.
  The look of your dialog card, the typography of your header — those are
  your decisions by default. When you want contrast-tested tokens and
  shallow style helpers, opt in to **Snora Design** (`features =
  ["widgets", "design"]`) — an iced-free token vocabulary with four
  built-in presets and WCAG AA–tested palettes.

## Read more

For the full picture, head to **[docs/](docs/)**:

- New to snora? Start with [Getting started](docs/src/getting-started/).
- Looking up something specific? [Reference](docs/src/reference/) and
  [Guides](docs/src/guides/).
- Wanting to contribute? [docs/src/contributing/](docs/src/contributing/) is
  for you — and we'd be glad to have you.

Per-release notes live in [CHANGELOG.md](CHANGELOG.md); the
direction of upcoming work is sketched in [ROADMAP.md](ROADMAP.md).

## Contributing and feedback

- **Bug reports and questions** — open an issue.
- **Feature requests** — use the
  [feature request template](https://github.com/nabbisen/snora/issues/new?template=feature-request.yml).
  Please read the scope guide first; Snora is intentionally narrow.
- **Using Snora in a real app?** Share your experience via the
  [downstream feedback template](https://github.com/nabbisen/snora/issues/new?template=downstream-feedback.yml).
  Real app stories are the strongest signal for 1.0 readiness.
- **PRs** — the most legible form of argument. See
  [docs/src/contributing/](docs/src/contributing/).

Built with [iced](https://iced.rs).
