User Interface
Design system, themes, and accessibility
The Gryt client is built with Radix Themes, Motion (formerly Framer Motion) for animations, and supports dark/light themes with system preference detection.
Tech Stack
- Radix Themes — component library (buttons, dialogs, cards, text fields, etc.)
- CSS variables — theming and color tokens
- Motion — layout animations and transitions (AnimatePresence, motion.div)
- React Icons — icon set (Material Design icons via
react-icons/md)
Theming
Gryt uses the Radix Themes <Theme> provider with configurable accent color, gray color, radius, and appearance (light/dark/system). The useTheme() hook exposes:
resolvedAppearance—"light"or"dark"after resolving system preferenceaccentColor— Radix accent color (e.g."iris","blue")grayColor— Radix gray variantradius— border radius scaleuiScale— global UI scaling factorchatFontSize— font size for chat messages
Theme preferences are persisted per user and sync across sessions.
CSS Variables
Custom variables extend the Radix theme tokens:
:root {
--default-font-family: "Atkinson Hyperlegible Next", sans-serif;
--code-font-family: "Atkinson Hyperlegible Mono", monospace;
--scaling: 1.15;
}
.dark {
--color-background: #111318;
--color-panel-solid: #1a1d24;
}
.light {
--color-background: #eef0f4;
--color-panel-solid: #ffffff;
}Typography
Gryt uses Atkinson Hyperlegible Next as its primary typeface and Atkinson Hyperlegible Mono for monospaced text. Created by the Braille Institute of America, these fonts maximize character differentiation for users with low vision while looking great for everyone. See the Accessibility page for more details.
Both fonts ship as variable WOFF2 files supporting weights 200–900.
Invites and Membership
Gryt servers are invite-only.
- The first user to join a brand-new server automatically becomes owner/admin.
- After that, new users must join via an invite link.
- Existing members can rejoin without an invite.
Admins can manage invites in Server settings > Invites:
- Create single-use or multi-use invites (max uses)
- Create infinite-use invites
- Revoke invites (revocation is permanent)
Each invite shows its usage:
- Finite:
uses remaining / max uses - Infinite:
uses consumed / ∞
Emoji System
Standard Emoji Shortcodes
Type :smile: or any other standard shortcode in the chat input and it will be rendered as the corresponding Unicode emoji. Over 1800 standard emojis are supported via the gemoji dataset, including names, aliases, and tags.
Custom Server Emojis
Server admins can upload custom emojis (PNG, JPEG, WebP, GIF, or SVG). Animated GIFs are preserved as GIFs; all other formats are resized to 128px height and converted to PNG. Multiple images can be selected at once, and .zip archives containing images are automatically extracted. Shortcodes are derived from filenames and deduplicated against existing server emojis. Custom emojis are used with the same :shortcode: syntax and render as inline images in messages.
Each emoji shows an individual upload progress bar during upload, which is especially useful for larger GIF files. After uploading, admins can rename any emoji by clicking the edit icon in the emoji list.
Emoji Autocomplete
When you type : followed by two or more characters, a popup appears above the chat input showing matching emojis. The search uses a ranked algorithm:
- Prefix match —
:smimatches:smile: - Word-boundary match —
:upmatches:thumbs_up: - Substring match —
:rinmatches:grinning: - Tag/alias match —
:happymatches emojis tagged "happy"
Navigate with arrow keys, press Enter/Tab to insert, or click a result. Custom server emojis are included and marked with a "custom" badge.
Rich Text Input
The chat input is a contenteditable editor that renders emojis inline. Standard emojis appear as native Unicode characters and custom emojis appear as small inline images, so you can see exactly what your message will look like before sending.
Application Structure
The client is organized into feature packages under src/packages/:
| Package | Purpose |
|---|---|
audio | Microphone, audio pipeline, RNNoise, push-to-talk, speakers |
common | Auth (Keycloak), shared hooks, utility functions |
dialogues | Dialog/modal components |
lib | Shared utilities |
mobile | Mobile-related hooks |
settings | Settings UI, theme, server management |
signUp | Sign-up/onboarding flow |
socket | Socket.IO connections, server list, member sidebar |
webRTC | SFU connection, voice room, camera, screen share |
Top-level components live in src/components/ and include the titlebar, browser banner, error boundary, welcome screen, and microphone debug overlay.
Accessibility
See the Accessibility guide for our commitment and current status. Key points:
- Atkinson Hyperlegible typeface for maximum legibility
- Radix UI components provide built-in ARIA attributes and keyboard interaction
- Dark/light themes with system preference detection
- Keyboard shortcuts for mute, deafen, disconnect, and push-to-talk
- Ongoing work on full keyboard navigation, screen reader support, and focus management