Fishies 0.8.11

This commit is contained in:
CRTkafa 2026-10-30 08:23:23 +00:00
parent 9020cffee7
commit ed00d055ae
23 changed files with 108 additions and 52 deletions

View file

@ -18,6 +18,11 @@ The kit the social aquarium game [Fishies](https://fishies.lol) is drawn with: r
components, design tokens as CSS variables and as data, and the painted system icon set. See every components, design tokens as CSS variables and as data, and the painted system icon set. See every
piece live, with the Fishies Handbook, at **https://fishies.lol/ui**. piece live, with the Fishies Handbook, at **https://fishies.lol/ui**.
<div align="center">
<img src="https://cdn.fishies.lol/media/readme/2026-10-29/ui-components.webp" alt="Fishies UI components: buttons, round buttons, chips, badges, tags, stats, cards and tabs" width="49%">
<img src="https://cdn.fishies.lol/media/readme/2026-10-29/ui-icons.webp" alt="The painted Fishies system icon set: care tools, places and lists" width="49%">
</div>
```sh ```sh
bun add @fishies/ui bun add @fishies/ui
``` ```
@ -32,7 +37,7 @@ import '@fishies/ui/kit.css' // the components
import { Button, Panel, configureIcons } from '@fishies/ui' import { Button, Panel, configureIcons } from '@fishies/ui'
// The icons ship in this package under icons/. Serve that folder, or point at a CDN copy: // The icons ship in this package under icons/. Serve that folder, or point at a CDN copy:
configureIcons({ url: icon => `https://cdn.jsdelivr.net/npm/@fishies/ui@0.8.10/icons/${icon.name}.png` }) configureIcons({ url: icon => `https://cdn.jsdelivr.net/npm/@fishies/ui@0.8.11/icons/${icon.name}.png` })
export function Shop() { export function Shop() {
return <Panel title="Shop" icon="shop"> return <Panel title="Shop" icon="shop">
@ -57,4 +62,4 @@ stylesheet that does, and every component animates with them; leave them out and
| `icons/` | the painted icon set, one PNG per name, each twice its largest display size | | `icons/` | the painted icon set, one PNG per name, each twice its largest display size |
| `handbook/` | the Fishies Handbook as Markdown | | `handbook/` | the Fishies Handbook as Markdown |
Version 0.8.10: the minor version follows the Fishies release it was taken from; a patch is a fix to the kit itself. Version 0.8.11: the minor version follows the Fishies release it was taken from; a patch is a fix to the kit itself.

View file

@ -222,20 +222,25 @@ Named slots the motion system fills (apps/web/app/motion/motion.css, where each
### Reef Journal ### Reef Journal
- [Open Water](https://fishies.lol/wiki/mechanics/open-water) · Swim out as one of your fish and meet other keepers. - [Open Water](https://fishies.lol/wiki/mechanics/open-water) · Swim out as one of your fish and meet other keepers.
- [Sea folk](https://fishies.lol/wiki/mechanics/npcs) · Ollie, Marlo, Captain Shelby and Pip live out in Open Water. Talk to them, make friends and hear their stories. - [Sea folk](https://fishies.lol/wiki/mechanics/npcs) · Ollie, Marlo, Captain Shelby, Pip and Lumen live out in Open Water. Talk to them, make friends and hear their stories.
- [Characters](https://fishies.lol/wiki/npcs) · The neighbours out in the sea: where each one lives, how they talk, what they give and what they ask for.
- [Creatures](https://fishies.lol/wiki/creatures) · The wild creatures of the sea that are not fish: the jellyfish and the golden shoal.
- [Lore](https://fishies.lol/wiki/lore) · The stories of the sea, told by the game: the Lantern Keepers and what they left behind.
- [Expeditions](https://fishies.lol/wiki/mechanics/expeditions) · Send a team of your grown fish to the Trench or the Vents and wait for what they bring home. - [Expeditions](https://fishies.lol/wiki/mechanics/expeditions) · Send a team of your grown fish to the Trench or the Vents and wait for what they bring home.
- [Swimming in a friend's aquarium](https://fishies.lol/wiki/mechanics/swim-visits) · Join another keeper's aquarium as one of your own fish. - [Swimming in a friend's aquarium](https://fishies.lol/wiki/mechanics/swim-visits) · Join another keeper's aquarium as one of your own fish.
- [Halloween](https://fishies.lol/wiki/mechanics/halloween) · Trick-or-treating at other keepers’ aquariums while the Halloween party runs. - [Halloween](https://fishies.lol/wiki/mechanics/halloween) · Trick-or-treating at other keepers’ aquariums while the Halloween party runs.
- [Places](https://fishies.lol/wiki/places) · Every Open Water place and every room behind a door: what stands there, what you find there and how to get there.
- [Events and seasons](https://fishies.lol/wiki/events) · The seasonal parties, the weekly contests and the things that happen out in the sea, with their dates, places and prizes.
### Places ### Places
- [Kelp Garden](https://fishies.lol/open-water/kelp-garden) · A coral garden that runs into a kelp forest. Room to roam. - [Kelp Garden](https://fishies.lol/open-water/kelp-garden) · A coral garden that runs into a kelp forest, home to the Gallery and the Pineapple House. Bite the kelp for Kelp Fibre.
- [Shipwreck](https://fishies.lol/open-water/shipwreck) · An old ship asleep on the sand, lanterns still lit. Swim through its ribs. - [Shipwreck](https://fishies.lol/open-water/shipwreck) · An old ship asleep on the sand, lanterns still lit. A chest is hidden along it every week, and its crates give Driftwood.
- [Market](https://fishies.lol/open-water/market) · A wanted board in the sunny shallows. Bring the fish the Market is asking for this week. - [Market](https://fishies.lol/open-water/market) · Stalls in the sunny shallows where keepers trade fish, eggs and food. The wanted board asks for new fish every week.
- [Lagoon](https://fishies.lol/open-water/lagoon) · Warm, bright shallows of seagrass and white sand. Play Pearl Catch at the oyster bed. - [Lagoon](https://fishies.lol/open-water/lagoon) · Warm, bright shallows of seagrass and white sand. Play Pearl Catch at the oyster bed and open the clams for Lagoon Shells.
- [Arena](https://fishies.lol/open-water/arena) · An open stadium of coral stands round a start gate. Race the week's course for a place on the board. - [Arena](https://fishies.lol/open-water/arena) · An open stadium of coral stands round a start gate. Race the week's course for a place on the board.
- [Ice Shelf](https://fishies.lol/open-water/ice-shelf) · Pale water under an ice roof, frost ferns and ice spires. The seasonal parties are thrown here. - [Ice Shelf](https://fishies.lol/open-water/ice-shelf) · Pale water under an ice roof, frost ferns and ice spires. The seasonal parties are thrown here, and the spires give Ice Crystals.
- [Ruins](https://fishies.lol/open-water/ruins) · A sunken temple among fallen columns, its door sealed. Six lost tablets are hidden across Open Water. - [Ruins](https://fishies.lol/open-water/ruins) · A sunken temple among fallen columns, its door sealed. Six lost tablets are hidden across Open Water, and the columns give Stone Shards.
- [Trench](https://fishies.lol/open-water/trench) · The dark deep at the reef's edge. Send a fish on an expedition into it from the diving bell. - [Trench](https://fishies.lol/open-water/trench) · The dark deep at the reef's edge. Send a fish on an expedition into it from the diving bell.
- [Vents](https://fishies.lol/open-water/vents) · Warm volcanic chimneys glowing in the dark. Send a fish on an expedition from the outpost. - [Vents](https://fishies.lol/open-water/vents) · Warm volcanic chimneys glowing in the dark. Send a fish on an expedition from the outpost.
- [Pineapple House](https://fishies.lol/open-water/pineapple-house) · A house made from a pineapple, deep in the kelp. It is the reading room: tap it to open the Reef Journal. - [Pineapple House](https://fishies.lol/open-water/pineapple-house) · A house made from a pineapple, deep in the kelp. It is the reading room: tap it to open the Reef Journal.
@ -245,6 +250,21 @@ Named slots the motion system fills (apps/web/app/motion/motion.css, where each
### Catalogues ### Catalogues
- [Collection pages](https://fishies.lol/wiki/collections) (24) · Every Reef Journal collection page: its pieces, where each one comes from and what finishing it pays.
- [Species catalogue](https://fishies.lol/wiki/species) (106) · Every fish in the game with its growth time, unlock level, egg price and sale value. - [Species catalogue](https://fishies.lol/wiki/species) (106) · Every fish in the game with its growth time, unlock level, egg price and sale value.
- [Quest log](https://fishies.lol/wiki/quests) (138) · Every quest in the game with its goals, its place in its chain and its reward.
- [Quest chains](https://fishies.lol/wiki/quest-chains) (16) · The storyline and every quest chain, step by step, with what opens each one and what it pays in all.
- [Achievement list](https://fishies.lol/wiki/achievements) (40) · Every achievement with its tier and reward. The hidden ones stay behind a spoiler.
- [Badges](https://fishies.lol/wiki/badges) (13) · Every badge a keeper can wear on a profile and how it is earned.
- [Decorations catalogue](https://fishies.lol/wiki/decorations) (74) · Every plant and ornament, what it costs and how long it keeps algae away. - [Decorations catalogue](https://fishies.lol/wiki/decorations) (74) · Every plant and ornament, what it costs and how long it keeps algae away.
- [Equipment catalogue](https://fishies.lol/wiki/equipment) (9) · Every filter, auto-feeder and heater with its price, unlock level and what it does. - [Equipment catalogue](https://fishies.lol/wiki/equipment) (10) · Every filter, auto-feeder and heater with its price, unlock level and what it does.
- [Items](https://fishies.lol/wiki/items) (90) · Every food, material, tool, wardrobe piece and prize: what it does, where it comes from and what it is used in.
- [Workshop recipes](https://fishies.lol/wiki/recipes) (21) · Every mix the Workshop's bowl makes, with its ingredients, time, level and what comes out.
- [Research tree](https://fishies.lol/wiki/research) (31) · Every study and mastery in the Research tree, with its branch, level, Notes, coins and what it opens.
- [Rooms, themes and floors](https://fishies.lol/wiki/styles) (23) · Every Room around the glass, every water theme and every floor, with how each one is unlocked.
- [Upgrades](https://fishies.lol/wiki/upgrades) (5) · The bench tiers, the expedition kits, more aquariums, enlarging and the slot ladder, tier by tier.
- [Characters](https://fishies.lol/wiki/npcs) (5) · The neighbours out in the sea: where each one lives, how they talk, what they give and what they ask for.
- [Creatures](https://fishies.lol/wiki/creatures) (2) · The wild creatures of the sea that are not fish: the jellyfish and the golden shoal.
- [Lore](https://fishies.lol/wiki/lore) (1) · The stories of the sea, told by the game: the Lantern Keepers and what they left behind.
- [Places](https://fishies.lol/wiki/places) (13) · Every Open Water place and every room behind a door: what stands there, what you find there and how to get there.
- [Events and seasons](https://fishies.lol/wiki/events) (11) · The seasonal parties, the weekly contests and the things that happen out in the sea, with their dates, places and prizes.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Before After
Before After

BIN
icons/research.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Before After
Before After

View file

@ -1,6 +1,6 @@
{ {
"name": "@fishies/ui", "name": "@fishies/ui",
"version": "0.8.10", "version": "0.8.11",
"description": "The Fishies UI kit: reef-toy components, design tokens and the painted system icon set from the social aquarium game Fishies.", "description": "The Fishies UI kit: reef-toy components, design tokens and the painted system icon set from the social aquarium game Fishies.",
"keywords": [ "keywords": [
"fishies", "fishies",

View file

@ -1,7 +1,7 @@
'use client' 'use client'
import { useCallback, useEffect, useLayoutEffect, useRef, useState, type ImgHTMLAttributes, type Ref } from 'react' import { useCallback, useEffect, useLayoutEffect, useRef, useState, type ImgHTMLAttributes, type Ref } from 'react'
import { reveal } from './fx-image' import { imageFallback, reveal } from './fx-image'
import './loader.css' import './loader.css'
const useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect const useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect
@ -23,9 +23,16 @@ export default function FishiesImg({ ref, decoding, ...props }: ImgHTMLAttribute
mounted.current = true mounted.current = true
reveal(img, first && img.complete) reveal(img, first && img.complete)
const settle = () => reveal(img) const settle = () => reveal(img)
delete img.dataset.webpFallback
const failed = () => {
const png = imageFallback(img.currentSrc || img.src)
if (png && !img.dataset.webpFallback) { img.dataset.webpFallback = '1'; img.src = png; return }
reveal(img)
}
img.addEventListener('load', settle) img.addEventListener('load', settle)
img.addEventListener('error', settle) img.addEventListener('error', failed)
return () => { img.removeEventListener('load', settle); img.removeEventListener('error', settle) } return () => { img.removeEventListener('load', settle); img.removeEventListener('error', failed) }
}, [props.src, props.srcSet]) }, [props.src, props.srcSet])
return <img {...props} ref={setRef} decoding={decoding ?? 'async'} data-fx="wait" suppressHydrationWarning/> return <img {...props} ref={setRef} decoding={decoding ?? 'async'} data-fx="wait" suppressHydrationWarning/>
} }

View file

@ -1,5 +1,9 @@
export type FxState = 'wait' | 'fade' | 'ready' | 'error' export type FxState = 'wait' | 'fade' | 'ready' | 'error'
let alternative: (src: string) => string | null = () => null
export function configureImageFallback(find: (src: string) => string | null): void { alternative = find }
export const imageFallback = (src: string): string | null => alternative(src)
export function reveal(img: HTMLImageElement, instant?: boolean): void { export function reveal(img: HTMLImageElement, instant?: boolean): void {
if (img.getAttribute('data-fx') !== 'wait' || !img.complete) return if (img.getAttribute('data-fx') !== 'wait' || !img.complete) return
const src = img.currentSrc || img.src const src = img.currentSrc || img.src

View file

@ -1,15 +1,15 @@
export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly sharpTo: number; readonly usedBy: readonly string[] }>> = { export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly sharpTo: number; readonly usedBy: readonly string[] }>> = {
"feed": { "feed": {
"px": 56, "px": 168,
"sharpTo": 28, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Reef Journal" "Reef Journal"
] ]
}, },
"net": { "net": {
"px": 56, "px": 168,
"sharpTo": 28, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS", "Keeper OS",
@ -17,8 +17,8 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
] ]
}, },
"clean": { "clean": {
"px": 56, "px": 168,
"sharpTo": 28, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS", "Keeper OS",
@ -26,8 +26,8 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
] ]
}, },
"decorate": { "decorate": {
"px": 56, "px": 168,
"sharpTo": 28, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Reef Journal" "Reef Journal"
@ -37,7 +37,8 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
"px": 168, "px": 168,
"sharpTo": 84, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game" "Game",
"Reef Journal"
] ]
}, },
"remedy": { "remedy": {
@ -68,8 +69,8 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
] ]
}, },
"explore": { "explore": {
"px": 56, "px": 168,
"sharpTo": 28, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS", "Keeper OS",
@ -86,8 +87,8 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
] ]
}, },
"shop": { "shop": {
"px": 56, "px": 168,
"sharpTo": 28, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS", "Keeper OS",
@ -112,8 +113,8 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
] ]
}, },
"inventory": { "inventory": {
"px": 56, "px": 168,
"sharpTo": 28, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS", "Keeper OS",
@ -147,16 +148,16 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
] ]
}, },
"pin": { "pin": {
"px": 40, "px": 168,
"sharpTo": 20, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS" "Keeper OS"
] ]
}, },
"menu": { "menu": {
"px": 52, "px": 168,
"sharpTo": 26, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS", "Keeper OS",
@ -164,8 +165,8 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
] ]
}, },
"bell": { "bell": {
"px": 52, "px": 168,
"sharpTo": 26, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS", "Keeper OS",
@ -192,8 +193,8 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
] ]
}, },
"sound": { "sound": {
"px": 52, "px": 168,
"sharpTo": 26, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS", "Keeper OS",
@ -202,8 +203,8 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
] ]
}, },
"mute": { "mute": {
"px": 52, "px": 168,
"sharpTo": 26, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS", "Keeper OS",
@ -211,17 +212,18 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
] ]
}, },
"music": { "music": {
"px": 52, "px": 168,
"sharpTo": 26, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game" "Game"
] ]
}, },
"fullscreen": { "fullscreen": {
"px": 52, "px": 168,
"sharpTo": 26, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Reef Journal",
"Video player" "Video player"
] ]
}, },
@ -310,7 +312,8 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
"sharpTo": 84, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS" "Keeper OS",
"Reef Journal"
] ]
}, },
"lock": { "lock": {
@ -375,6 +378,7 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
"sharpTo": 84, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS",
"Reef Journal" "Reef Journal"
] ]
}, },
@ -424,6 +428,7 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
"sharpTo": 84, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS",
"Reef Journal" "Reef Journal"
] ]
}, },
@ -438,7 +443,8 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
"px": 168, "px": 168,
"sharpTo": 84, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game" "Game",
"Reef Journal"
] ]
}, },
"block": { "block": {
@ -498,7 +504,8 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
"px": 168, "px": 168,
"sharpTo": 84, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game" "Game",
"Reef Journal"
] ]
}, },
"thermometer": { "thermometer": {
@ -520,7 +527,8 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
"px": 168, "px": 168,
"sharpTo": 84, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game" "Game",
"Keeper OS"
] ]
}, },
"gift": { "gift": {
@ -551,15 +559,17 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
"sharpTo": 84, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS" "Keeper OS",
"Reef Journal"
] ]
}, },
"quest": { "quest": {
"px": 168, "px": 320,
"sharpTo": 84, "sharpTo": 160,
"usedBy": [ "usedBy": [
"Game", "Game",
"Keeper OS" "Keeper OS",
"Reef Journal"
] ]
}, },
"check": { "check": {
@ -620,7 +630,8 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
"px": 168, "px": 168,
"sharpTo": 84, "sharpTo": 84,
"usedBy": [ "usedBy": [
"Game" "Game",
"Keeper OS"
] ]
}, },
"journal": { "journal": {
@ -630,6 +641,14 @@ export const ICON_FACTS: Readonly<Record<string, { readonly px: number; readonly
"Game" "Game"
] ]
}, },
"research": {
"px": 320,
"sharpTo": 160,
"usedBy": [
"Game",
"Reef Journal"
]
},
"add": { "add": {
"px": 168, "px": 168,
"sharpTo": 84, "sharpTo": 84,

View file

@ -58,6 +58,7 @@ export const SYSTEM_ICONS = [
ui('keyboard', 'Keys', 'system', 'shortcuts and keys'), ui('external', 'Opens elsewhere', 'system', 'opens in a new tab'), ui('keyboard', 'Keys', 'system', 'shortcuts and keys'), ui('external', 'Opens elsewhere', 'system', 'opens in a new tab'),
ui('journal', 'Reef Journal', 'places', 'the Reef Journal notebook'), ui('journal', 'Reef Journal', 'places', 'the Reef Journal notebook'),
ui('research', 'Research', 'places', 'the Research app: studies, levers and the tree'),
ui('add', 'New', 'system', 'new, create'), ui('download', 'Download', 'system', 'download'), ui('mail', 'Mail', 'system', 'email, the newsletter'), ui('add', 'New', 'system', 'new, create'), ui('download', 'Download', 'system', 'download'), ui('mail', 'Mail', 'system', 'email, the newsletter'),
emote('hello', 'Hello', 'wave hello'), emote('heart', 'Love', 'send love'), emote('question', 'Question', 'wonder aloud'), emote('surprise', 'Surprise', 'be surprised'), emote('hello', 'Hello', 'wave hello'), emote('heart', 'Love', 'send love'), emote('question', 'Question', 'wonder aloud'), emote('surprise', 'Surprise', 'be surprised'),
emote('food', 'Food', 'ask for food'), emote('sleep', 'Sleepy', 'feel sleepy'), emote('music', 'Music', 'enjoy the music'), emote('sparkle', 'Sparkle', 'something wonderful'), emote('food', 'Food', 'ask for food'), emote('sleep', 'Sleepy', 'feel sleepy'), emote('music', 'Music', 'enjoy the music'), emote('sparkle', 'Sparkle', 'something wonderful'),