videorc/ui

Icons

The semantic icon set. Components ask for a meaning, never a glyph, so you swap an icon or the whole set in one file. Phosphor underneath.

Installation

$ pnpm dlx shadcn@latest add @videorc/icons

This adds components/icons.tsx and installs @phosphor-icons/react.

Meaning over shape

Every videorc/ui component imports its icons from @/components/icons, by meaning: SourcesIcon, AlertIcon, RecordIcon. Nothing imports @phosphor-icons/react directly.

  • Swapping is a one-line change. Point SourcesIcon at a different glyph and every component that shows sources follows.
  • The set stays countable. 97 slots over 93 glyphs, reviewable in one file.
  • One glyph per meaning. A triangle warns (WarningIcon), a circle alerts (AlertIcon), a crossed circle is an error (ErrorIcon). Before adding a slot, check whether one already means what you need; never add a second variant of a meaning.
  • Copy names what an icon shows, never its slot: "Trash", not "DeleteIcon".

Usage

import { RecordIcon, SettingsIcon } from "@/components/icons";

Inside a button, mark an icon's position with data-icon so the padding adjusts. The button sizes its icons: 16 px, or 12 px at xs.

<Button variant="record">
  <RecordIcon data-icon="inline-start" weight="fill" />
  Record
</Button>

An icon-only button needs an aria-label. Icons inside a labelled button are decorative.

<Button aria-label="Settings" size="icon" variant="ghost">
  <SettingsIcon />
</Button>

weight is Phosphor's: thin, light, regular (the default), bold, fill or duotone. Videorc uses fill for the active page in navigation and for status icons. A single-weight set can accept weight and ignore it.

Inline and status icons are 16 px and take the secondary text color unless they carry a status. Icons draw in currentColor, so they follow their text.

The set

The slots are grouped as they are in components/icons.tsx. Some slots share a glyph today (StudioIcon and CameraIcon, SourcesIcon and DisplayIcon, AssetsIcon and ImageIcon, OutputIcon and RecordIcon); they're separate slots so they can diverge without touching a component.

One slot per sidebar destination in Videorc.

SlotGlyphMeaning
StudioIconVideoCameraThe Studio, where you record and go live
SourcesIconMonitorSources: screens, windows, cameras, microphones
SceneIconSquaresFourScenes and their layouts
AssetsIconImageSquareAssets: the media you put on stream
LivestreamIconBroadcastLivestream setup and destinations
CaptionsIconClosedCaptioningCaptions
OutputIconRecordOutput: where recordings go
LibraryIconFilmReelThe library of recordings
SettingsIconGearSixSettings
HealthIconPulseHealth: whether everything is working

Status

SlotGlyphMeaning
WarningIconWarningNeeds attention. The triangle warns
AlertIconWarningCircleA notice to read. The circle alerts
ErrorIconXCircleIt failed
SuccessIconCheckCircleIt finished, or it's healthy
CheckIconCheckChecked, chosen, copied
InfoIconInfoMore information
SpinnerIconCircleNotchWork in progress, spinning
HeartbeatIconHeartbeatA live signal, such as stream health
GaugeIconGaugeA measured level, such as bitrate
DisabledIconMinusCircleOff or unavailable

Chrome and controls

SlotGlyphMeaning
ChevronDownIconCaretDownOpens downward: a select, a collapsible
ChevronUpIconCaretUpCollapses, or scrolls up
ChevronRightIconCaretRightGoes deeper: a row that navigates, a submenu
CloseIconXCloses a dialog, a toast, a panel
MoreIconDotsThreeMore actions, the ⋯ menu
SearchIconMagnifyingGlassFinds
ZoomInIconMagnifyingGlassPlusOpens a small picture larger. Never a zoom level control
AdjustIconSlidersHorizontalAdjusts: filters, levels, options
LayoutIconLayoutA layout

Arrows and movement

SlotGlyphMeaning
ArrowUpIconArrowUpUp, move up
ArrowDownIconArrowDownDown, move down
ArrowLeftIconArrowLeftBack
ArrowRightIconArrowRightForward, next
ExternalLinkIconArrowSquareOutOpens outside the app, in the browser
RefreshIconArrowClockwiseReload this
SyncIconArrowsClockwiseSync with somewhere else
ResetIconArrowCounterClockwiseBack to the default
SortIconArrowsDownUpSort order

Capture, media and playback

SlotGlyphMeaning
CameraIconVideoCameraA camera
DisplayIconMonitorA display or a screen to capture
DesktopIconDesktopThis computer
MobileIconDeviceMobileA phone
MicrophoneIconMicrophoneA microphone
SpeakerOnIconSpeakerHighSound on
SpeakerOffIconSpeakerSlashSound off
WaveformIconWaveformAudio, a level
WaveformMutedIconWaveformSlashAudio muted
RecordIconRecordRecord
StopIconStopStops capture: a recording or a livestream
PlayIconPlayPlays
PauseIconPausePauses a player. Stop is the capture action
VideoFileIconFileVideoA video file
ClapperboardIconFilmSlateA recording
FrameIconFrameCornersThe output frame
CrosshairIconCrosshairA position or a target
MarkerIconBookmarkSimpleA marker: a point on a recording to find again. Never scissors
HiddenIconEyeSlashThe screen is hidden from viewers. Never the eye that opens the preview

Files, assets and editing

SlotGlyphMeaning
FolderIconFolderOpenA folder, or showing a file in the file manager
DownloadIconDownloadSimpleDownload, export
UploadIconUploadSimpleUpload, import
SaveIconFloppyDiskSave
DeleteIconTrashDelete
CopyIconCopyCopy
EditIconPencilSimpleEdit, rename
NoteIconNotePencilNotes
ImageIconImageSquareAn image
ImageBrokenIconImageBrokenAn image that couldn't load
PreviewIconEyeThe preview window
SlotGlyphMeaning
LockIconLockKeyLocked
SignInIconSignInSign in
SignOutIconSignOutSign out
AccountIconUserCircleYour account
LinkIconLinkSimpleA link

Livestream audience

What viewers do for the stream, as the Stream Manager's stats bar and Activity show it.

SlotGlyphMeaning
ViewersIconUserPeople watching now. Never your own account

AI, tooling and appearance

SlotGlyphMeaning
SparkleIconSparkleAn AI feature
ClipIconScissorsCutting only: clips, a clean cut
RepairIconWrenchRepair, fix
BugIconBugReport a bug
TerminalIconTerminalWindowLogs, a developer console
TextIconTextAaText and typography
ChatIconChatCircleChat
SendIconPaperPlaneRightSend
PinIconPushPinPin, keep on top
LightModeIconSunLight appearance
DarkModeIconMoonDark appearance

Platform marks

SlotGlyphMeaning
YoutubeIconYoutubeLogoYouTube
XPlatformIconXLogoX
TwitchIconTwitchLogoTwitch
TiktokIconTiktokLogoTikTok
InstagramIconInstagramLogoInstagram
FacebookIconFacebookLogoFacebook

Platform and source marks are the only full-color icons on screen. The theme carries brand tints for them (text-platform-youtube, text-platform-twitch, text-platform-instagram, text-platform-facebook). Each mark is its owner's trademark, with its own rules: see License.

AppIcon and AppIconProps

export type AppIconProps = {
  size?: number | string;
  weight?: "thin" | "light" | "regular" | "bold" | "fill" | "duotone";
} & Omit<SVGProps<SVGSVGElement>, "ref" | "weight">;

export type AppIcon = ComponentType<AppIconProps>;

AppIcon is the type of every slot. Use it wherever you store an icon, such as a nav item or a row's metadata:

import type { AppIcon } from "@/components/icons";
import { CameraIcon, MicrophoneIcon } from "@/components/icons";

const devices: { icon: AppIcon; label: string }[] = [
  { icon: CameraIcon, label: "Camera" },
  { icon: MicrophoneIcon, label: "Microphone" },
];

The type is declared in the file, not re-exported from Phosphor, so your code depends on your own contract. The file's IconSetCheck type proves at compile time that every Phosphor glyph it exports accepts AppIconProps.

Swapping a glyph or the whole set

The export names are the contract. Change what's behind a name in your copy of components/icons.tsx, and every component keeps working as long as each name still exports an AppIcon.

To swap one glyph, point the slot at another Phosphor icon, and add that icon's name to the IconSetCheck list so the compiler checks it:

components/icons.tsx
export {
  Plug as SourcesIcon,
  // …
} from "@phosphor-icons/react";

To use your own artwork, export a component under the slot's name. Draw in currentColor so it follows its text, and accept weight even if you ignore it:

components/icons.tsx
export function SourcesIcon({
  size = "1em",
  weight: _weight,
  ...props
}: AppIconProps) {
  return (
    <svg
      fill="currentColor"
      height={size}
      viewBox="0 0 24 24"
      width={size}
      {...props}
    >
      <path d="…" />
    </svg>
  );
}

To swap the whole set, rewrite the exports against your icon package and keep every name. When a newer component needs a slot you don't export, your typecheck tells you.

Once you've swapped anything, stop re-adding @videorc/icons with --overwrite; bring new slots in by hand. Videorc does exactly this: its copy replaces the YouTube and X slots with each platform's official artwork.

License

  • Phosphor is MIT. The registry can ship it, and you can redistribute it in your app.
  • Why not a licensed set. Videorc looked at Nucleo, whose open-source terms cap a project at 100 icons and forbid sharing or redistributing the icons. A public registry can't ship that, so the registry stays on Phosphor. A licensed set can only live in your own app's copy of components/icons.tsx, under your license. The same cap is one reason the set is kept small and countable.
  • Brand marks are trademarks, not MIT. Follow each platform's brand guidelines. Videorc, for example, draws YouTube's official icon at 20 px or more and never tints it, and draws X's mark from X's brand kit in pure black or white.