Icon
Sakana Element bundles all 486 icons from pixelarticons — a pixel-art style icon library on a 24×24 grid. Every icon is available out of the box with no extra installation.
| Name | Category | Description | Type | Default |
|---|---|---|---|---|
flip | Style | Flip direction | 'horizontal' | 'vertical' | 'both' | — |
rotation | Style | Rotation angle | 90 | 180 | 270 | — |
size | Size | Icon size | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '1x' | '2x' | '3x' | 'md' |
type | Color | Icon type color | 'primary' | 'success' | 'warning' | 'danger' | 'info' | — |
color | Color | Custom color | string | — |
spin | Behavior | Spin animation (step-based) | boolean | false |
pulse | Behavior | Pulse animation (spin + scale) | boolean | false |
bounce | Behavior | Bounce animation (vertical) | boolean | false |
shake | Behavior | Shake animation (horizontal) | boolean | false |
beat | Behavior | Beat animation (scale pulse) | boolean | false |
fade | Behavior | Fade animation (opacity blink) | boolean | false |
icon | Content | Icon name (pixelarticons name or mapped alias) | string | — |
Basic Usage
Use the icon property to specify the icon name. Icon names follow pixelarticons naming conventions. Common aliases are also supported — for example, spinner resolves to loader, and x resolves to close.
<template>
<div class="demo-icon">
<px-icon icon="home" />
<px-icon icon="user" />
<px-icon icon="search" />
<px-icon icon="bookmark" />
<px-icon icon="heart" />
<px-icon icon="sliders" />
</div>
</template>
<style scoped>
.demo-icon {
display: flex;
gap: 20px;
font-size: 24px;
}
</style>Different Sizes
Use the size property to set the icon size. All sizes are multiples of the 24px grid to ensure crisp pixel rendering:
| Size | Pixels | Description |
|---|---|---|
xs | 12px | Extra small |
sm | 18px | Small |
md | 24px | Default |
lg | 36px | Large |
xl | 48px | Extra large |
2xl | 72px | 2× extra large |
1x / 2x / 3x | 24 / 48 / 72px | FontAwesome-compatible aliases |
<template>
<div class="demo-icon">
<px-icon icon="bookmark" size="xs" />
<px-icon icon="bookmark" size="sm" />
<px-icon icon="bookmark" size="lg" />
<px-icon icon="bookmark" size="xl" />
<px-icon icon="bookmark" size="2xl" />
<px-icon icon="bookmark" size="3x" />
</div>
</template>
<style scoped>
.demo-icon {
display: flex;
align-items: center;
gap: 20px;
}
</style>Icon Colors
Use the type property for semantic colors (primary, success, warning, danger, info), or the color property for any custom color value. The color prop overrides type when both are set.
<template>
<div class="demo-icon">
<px-icon icon="check" type="primary" size="2x" />
<px-icon icon="check" type="success" size="2x" />
<px-icon icon="warning-box" type="warning" size="2x" />
<px-icon icon="close-box" type="danger" size="2x" />
<px-icon icon="info-box" type="info" size="2x" />
<px-icon icon="heart" color="#e91e63" size="2x" />
</div>
</template>
<style scoped>
.demo-icon {
display: flex;
align-items: center;
gap: 20px;
}
</style>Flip & Rotation
Use flip to mirror the icon (horizontal, vertical, or both). Use rotation to rotate by 90, 180, or 270 degrees. These can be combined.
<template>
<div class="demo-icon-flip">
<h4>Flip / 翻转</h4>
<div class="demo-row">
<div class="icon-item">
<px-icon icon="arrow-right" size="2x" />
<span>original</span>
</div>
<div class="icon-item">
<px-icon icon="arrow-right" size="2x" flip="horizontal" />
<span>horizontal</span>
</div>
<div class="icon-item">
<px-icon icon="arrow-right" size="2x" flip="vertical" />
<span>vertical</span>
</div>
<div class="icon-item">
<px-icon icon="arrow-right" size="2x" flip="both" />
<span>both</span>
</div>
</div>
<h4>Rotation / 旋转</h4>
<div class="demo-row">
<div class="icon-item">
<px-icon icon="arrow-right" size="2x" />
<span>0°</span>
</div>
<div class="icon-item">
<px-icon icon="arrow-right" size="2x" :rotation="90" />
<span>90°</span>
</div>
<div class="icon-item">
<px-icon icon="arrow-right" size="2x" :rotation="180" />
<span>180°</span>
</div>
<div class="icon-item">
<px-icon icon="arrow-right" size="2x" :rotation="270" />
<span>270°</span>
</div>
</div>
</div>
</template>
<style scoped>
.demo-icon-flip h4 {
margin: 0 0 12px;
font-size: 14px;
color: #888;
}
.demo-icon-flip h4:not(:first-child) {
margin-top: 20px;
}
.demo-row {
display: flex;
align-items: center;
gap: 30px;
}
.icon-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.icon-item span {
font-size: 12px;
color: #666;
}
</style>Icon Animations
All animations use CSS steps() for a discrete frame-by-frame look that matches the pixel-art aesthetic:
| Animation | Effect | Frames |
|---|---|---|
spin | 360° rotation | 8 steps |
pulse | Rotation + scale | 8 steps |
bounce | Vertical hop | 5 steps |
shake | Horizontal shake | 4 steps |
beat | Scale throb | 3 steps |
fade | Opacity flicker | 4 steps |
<template>
<div class="demo-icon">
<div class="icon-item">
<px-icon icon="loader" spin size="2x" />
<span>spin</span>
</div>
<div class="icon-item">
<px-icon icon="loader" pulse size="2x" />
<span>pulse</span>
</div>
<div class="icon-item">
<px-icon icon="notification" shake size="2x" />
<span>shake</span>
</div>
<div class="icon-item">
<px-icon icon="heart" beat size="2x" type="danger" />
<span>beat</span>
</div>
<div class="icon-item">
<px-icon icon="zap" bounce size="2x" type="warning" />
<span>bounce</span>
</div>
<div class="icon-item">
<px-icon icon="circle" fade size="2x" />
<span>fade</span>
</div>
</div>
</template>
<style scoped>
.demo-icon {
display: flex;
align-items: center;
gap: 30px;
}
.icon-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.icon-item span {
font-size: 12px;
color: #666;
}
</style>Icon Collection
Browse and search all 486 bundled icons. Click any icon to copy its name.
<script setup lang="ts">
import { defaultPixelIcons } from 'sakana-element';
import { computed, ref } from 'vue';
const search = ref('');
const copiedName = ref('');
const allIconNames = computed(() => Object.keys(defaultPixelIcons).sort());
const filteredIcons = computed(() => {
const keyword = search.value.trim().toLowerCase();
if (!keyword) return allIconNames.value;
return allIconNames.value.filter((name) => name.toLowerCase().includes(keyword));
});
function copyIconName(name: string) {
navigator.clipboard.writeText(name).then(() => {
copiedName.value = name;
setTimeout(() => {
copiedName.value = '';
}, 1500);
});
}
</script>
<template>
<div class="icon-collection">
<div class="icon-search">
<px-icon icon="search" size="sm" class="search-icon" />
<input
v-model="search"
type="text"
class="search-input"
placeholder="Search icon name..."
/>
<span class="icon-count">{{ filteredIcons.length }} icons</span>
</div>
<div v-if="filteredIcons.length" class="icon-grid">
<div
v-for="name in filteredIcons"
:key="name"
class="icon-card"
:class="{ 'icon-card--copied': copiedName === name }"
:title="`Click to copy: ${name}`"
@click="copyIconName(name)"
>
<px-icon :icon="name" size="lg" />
<span class="icon-name">{{ name }}</span>
<span v-if="copiedName === name" class="copied-tip">Copied!</span>
</div>
</div>
<div v-else class="icon-empty">
<px-icon icon="search" size="2x" />
<p>No icons found for "{{ search }}"</p>
</div>
</div>
</template>
<style scoped>
.icon-collection {
width: 100%;
}
.icon-search {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border: 2px solid var(--px-color-border, #ccc);
background: var(--px-color-bg, #fff);
margin-bottom: 16px;
}
.search-icon {
color: var(--px-color-text-placeholder, #999);
flex-shrink: 0;
}
.search-input {
flex: 1;
border: none;
outline: none;
background: transparent;
font-family: var(--px-font-family, monospace);
font-size: 14px;
color: var(--px-color-text, #333);
}
.search-input::placeholder {
color: var(--px-color-text-placeholder, #999);
}
.icon-count {
font-size: 12px;
color: var(--px-color-text-secondary, #888);
white-space: nowrap;
flex-shrink: 0;
}
.icon-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 8px;
max-height: 520px;
overflow-y: auto;
padding: 4px;
}
.icon-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
padding: 12px 4px 8px;
border: 2px solid var(--px-color-border-light, #e0e0e0);
background: var(--px-color-bg, #fff);
cursor: pointer;
position: relative;
transition: border-color 0.15s, background-color 0.15s;
}
.icon-card:hover {
border-color: var(--px-color-primary, #7287fd);
background: var(--px-color-primary-light-9, #eef1fe);
}
.icon-card--copied {
border-color: var(--px-color-success, #40a070);
background: var(--px-color-success-light-9, #e2f7eb);
}
.icon-name {
font-size: 10px;
color: var(--px-color-text-secondary, #888);
text-align: center;
word-break: break-all;
line-height: 1.3;
max-width: 100%;
}
.copied-tip {
position: absolute;
top: 2px;
right: 4px;
font-size: 10px;
color: var(--px-color-success, #40a070);
font-weight: bold;
}
.icon-empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
padding: 40px;
color: var(--px-color-text-placeholder, #999);
}
.icon-empty p {
margin: 0;
font-size: 14px;
}
</style>Custom Icons
Register your own SVG icons and use them alongside the built-in set.
Single Registration
import { registerPixelIcon } from 'sakana-element';
registerPixelIcon('my-icon', '<svg viewBox="0 0 24 24">...</svg>');<px-icon icon="my-icon" />Batch Registration
import { registerPixelIcons } from 'sakana-element';
registerPixelIcons({
'my-icon-a': '<svg viewBox="0 0 24 24">...</svg>',
'my-icon-b': '<svg viewBox="0 0 24 24">...</svg>',
});Registry API
| Function | Description |
|---|---|
registerPixelIcon(name, svg) | Register a single custom icon |
registerPixelIcons(icons) | Register multiple icons at once |
hasPixelIcon(name) | Check if an icon name is registered |
getRegisteredIconNames() | Get an array of all registered icon names |
getPixelIcon(name) | Get the SVG string of a registered icon |
resolveIconName(name) | Resolve an icon name alias to its pixelarticons name |
getIconNameMap() | Get the full icon name alias mapping |
TIP
SVG content is automatically sanitized on registration — dangerous elements like <script> and event handler attributes are stripped for XSS safety.