diff --git a/contexts/frontend.md b/contexts/frontend.md
index 46224b4..13ca78d 100644
--- a/contexts/frontend.md
+++ b/contexts/frontend.md
@@ -32,6 +32,16 @@ Defined in `server/src/wled_controller/static/css/base.css`.
| `--success-color` | `#28a745` | `#2e7d32` | Success indicators |
| `--shadow-color` | `rgba(0,0,0,0.3)` | `rgba(0,0,0,0.12)` | Box shadows |
+## Icons — No Emoji (IMPORTANT)
+
+**NEVER use emoji characters (`🔗`, `📋`, `🔍`, etc.) in buttons, labels, or card metadata.** Always use SVG icons from `core/icons.ts` (which wraps Lucide icon paths from `core/icon-paths.ts`).
+
+- Import the constant: `import { ICON_CLONE } from '../core/icons.ts'`
+- Use in template literals: `` `` ``
+- To add a new icon: copy inner SVG elements from [Lucide](https://lucide.dev) into `icon-paths.ts`, then export a named constant in `icons.ts`
+
+Emoji render inconsistently across OS, break monochrome icon themes, and cannot be styled with CSS `color`/`stroke`.
+
## UI Conventions for Dialogs
### Hints
diff --git a/server/src/wled_controller/static/js/features/automations.ts b/server/src/wled_controller/static/js/features/automations.ts
index cab1f30..8c8389b 100644
--- a/server/src/wled_controller/static/js/features/automations.ts
+++ b/server/src/wled_controller/static/js/features/automations.ts
@@ -9,7 +9,7 @@ import { showToast, showConfirm, setTabRefreshing } from '../core/ui.ts';
import { Modal } from '../core/modal.ts';
import { CardSection } from '../core/card-sections.ts';
import { updateTabBadge, updateSubTabHash } from './tabs.ts';
-import { ICON_SETTINGS, ICON_START, ICON_PAUSE, ICON_CLOCK, ICON_AUTOMATION, ICON_HELP, ICON_OK, ICON_TIMER, ICON_MONITOR, ICON_RADIO, ICON_SCENE, ICON_CLONE, ICON_TRASH, ICON_CIRCLE_OFF, ICON_UNDO } from '../core/icons.ts';
+import { ICON_SETTINGS, ICON_START, ICON_PAUSE, ICON_CLOCK, ICON_AUTOMATION, ICON_HELP, ICON_OK, ICON_TIMER, ICON_MONITOR, ICON_RADIO, ICON_SCENE, ICON_CLONE, ICON_TRASH, ICON_CIRCLE_OFF, ICON_UNDO, ICON_WEB } from '../core/icons.ts';
import * as P from '../core/icon-paths.ts';
import { wrapCard } from '../core/card-colors.ts';
import { TagInput, renderTagChips } from '../core/tag-input.ts';
@@ -247,7 +247,7 @@ const CONDITION_PILL_RENDERERS: Record = {
return `${ICON_MONITOR} ${t('automations.condition.display_state')}: ${stateLabel}`;
},
mqtt: (c) => `${ICON_RADIO} ${t('automations.condition.mqtt')}: ${escapeHtml(c.topic || '')} = ${escapeHtml(c.payload || '*')}`,
- webhook: (c) => `🔗 ${t('automations.condition.webhook')}`,
+ webhook: (c) => `${ICON_WEB} ${t('automations.condition.webhook')}`,
};
function createAutomationCard(automation: Automation, sceneMap = new Map()) {
diff --git a/server/src/wled_controller/static/js/features/color-strips-notification.ts b/server/src/wled_controller/static/js/features/color-strips-notification.ts
index 48aa061..121685e 100644
--- a/server/src/wled_controller/static/js/features/color-strips-notification.ts
+++ b/server/src/wled_controller/static/js/features/color-strips-notification.ts
@@ -7,7 +7,7 @@ import { fetchWithAuth, escapeHtml } from '../core/api.ts';
import { t } from '../core/i18n.ts';
import { showToast } from '../core/ui.ts';
import {
- ICON_SEARCH,
+ ICON_SEARCH, ICON_CLONE,
} from '../core/icons.ts';
import * as P from '../core/icon-paths.ts';
import { IconSelect } from '../core/icon-select.ts';
@@ -272,6 +272,6 @@ export function showNotificationEndpoint(cssId: any) {
const url = `${base}/color-strip-sources/${cssId}/notify`;
el.innerHTML = `
POST
-
+
`;
}
diff --git a/server/src/wled_controller/static/js/features/color-strips.ts b/server/src/wled_controller/static/js/features/color-strips.ts
index f8c9042..f9597b6 100644
--- a/server/src/wled_controller/static/js/features/color-strips.ts
+++ b/server/src/wled_controller/static/js/features/color-strips.ts
@@ -1862,9 +1862,9 @@ function _showApiInputEndpoints(cssId: any) {
const wsUrl = `${wsBase}/color-strip-sources/${cssId}/ws?token=${encodeURIComponent(apiKey)}`;
el.innerHTML = `
REST POST
-
+
WebSocket
-
+
`;
}
diff --git a/server/src/wled_controller/static/js/features/scene-presets.ts b/server/src/wled_controller/static/js/features/scene-presets.ts
index 49bc19c..4aacf18 100644
--- a/server/src/wled_controller/static/js/features/scene-presets.ts
+++ b/server/src/wled_controller/static/js/features/scene-presets.ts
@@ -9,7 +9,7 @@ import { showToast, showConfirm } from '../core/ui.ts';
import { Modal } from '../core/modal.ts';
import { CardSection } from '../core/card-sections.ts';
import {
- ICON_SCENE, ICON_CAPTURE, ICON_START, ICON_EDIT, ICON_REFRESH, ICON_TARGET, ICON_CLONE, ICON_TRASH,
+ ICON_SCENE, ICON_CAPTURE, ICON_START, ICON_EDIT, ICON_REFRESH, ICON_TARGET, ICON_CLONE, ICON_TRASH, ICON_LINK,
} from '../core/icons.ts';
import { scenePresetsCache, outputTargetsCache, automationsCacheObj } from '../core/state.ts';
import { TagInput, renderTagChips } from '../core/tag-input.ts';
@@ -82,7 +82,7 @@ export function createSceneCard(preset: ScenePreset) {
const meta = [
targetCount > 0 ? `${ICON_TARGET} ${targetCount} ${t('scenes.targets_count')}` : null,
- usedByCount > 0 ? `🔗 ${t('scene_preset.used_by').replace('%d', usedByCount)}` : null,
+ usedByCount > 0 ? `${ICON_LINK} ${t('scene_preset.used_by').replace('%d', usedByCount)}` : null,
].filter(Boolean);
const updated = preset.updated_at ? new Date(preset.updated_at).toLocaleString() : '';