Qaid
ARTICLE

Custom Button Icons

Replace the default thumbs up/down icons with custom SVGs, emoji, or any HTML content to match your brand's visual language.

Qaid Team

positiveIcon and negativeIcon take an HTML string. Any SVG, any emoji, anything you can write markup for. Thumbs are just the default.

What you start with

Default Icons

Standard thumbs up/down

Loading embed...

Fine for most sites. Wrong for some, which is what the rest of this is about.

SVG icons

Paste the markup straight in.

Heart and broken heart

For anywhere the reaction is felt rather than judged:

Heart Icons

Love it or hate it

Loading embed...
new QaidFeedback({
  endpoint: '/api/feedback',
  positiveIcon: `<svg viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
    <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
  </svg>`,
  negativeIcon: `<svg viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
    <path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
    <line x1="4" y1="4" x2="20" y2="20" stroke="currentColor" stroke-width="2"/>
  </svg>`,
  colors: {
    positive: "#ec4899",
    negative: "#6b7280"
  }
});

Stars

Where readers already expect to be rating something:

Star Icons

Star rating style

Loading embed...
new QaidFeedback({
  endpoint: '/api/feedback',
  positiveIcon: `<svg viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
    <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
  </svg>`,
  negativeIcon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="24" height="24">
    <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
  </svg>`,
  colors: {
    positive: "#fbbf24",
    negative: "#6b7280"
  }
});

Check and cross

No emotional reading at all, which suits an approval queue:

Check / X Icons

Clear approval indicators

Loading embed...
new QaidFeedback({
  endpoint: '/api/feedback',
  positiveIcon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" width="24" height="24">
    <polyline points="20 6 9 17 4 12"/>
  </svg>`,
  negativeIcon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" width="24" height="24">
    <line x1="18" y1="6" x2="6" y2="18"/>
    <line x1="6" y1="6" x2="18" y2="18"/>
  </svg>`,
  colors: {
    positive: "#22c55e",
    negative: "#ef4444"
  }
});

Arrows

The convention everyone learned from Reddit and Stack Overflow:

Arrow Icons

Upvote/downvote style

Loading embed...
new QaidFeedback({
  endpoint: '/api/feedback',
  positiveIcon: `<svg viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
    <path d="M12 4l-8 8h5v8h6v-8h5l-8-8z"/>
  </svg>`,
  negativeIcon: `<svg viewBox="0 0 24 24" fill="currentColor" width="24" height="24">
    <path d="M12 20l8-8h-5V4H9v8H4l8 8z"/>
  </svg>`,
  colors: {
    positive: "#ff4500",
    negative: "#7193ff"
  }
});

Emoji

The property takes HTML, and an emoji is a character, so this needs no SVG at all.

Faces

Emoji Faces

Simple and universal

Loading embed...
new QaidFeedback({
  endpoint: '/api/feedback',
  positiveIcon: '<span style="font-size: 24px">😊</span>',
  negativeIcon: '<span style="font-size: 24px">😞</span>'
});

Thumbs, but emoji

The default shape rendered in your reader's own emoji font:

Thumbs Emoji

Emoji thumbs

Loading embed...
new QaidFeedback({
  endpoint: '/api/feedback',
  positiveIcon: '<span style="font-size: 24px">👍</span>',
  negativeIcon: '<span style="font-size: 24px">👎</span>'
});

Fire and ice

When you want the two ends far apart:

Fire & Ice

Hot or cold reactions

Loading embed...
new QaidFeedback({
  endpoint: '/api/feedback',
  positiveIcon: '<span style="font-size: 24px">🔥</span>',
  negativeIcon: '<span style="font-size: 24px">🥶</span>',
  colors: {
    positive: "#f97316",
    negative: "#3b82f6"
  }
});

Rocket and crash

Ship it or sink it:

Rocket & Crash

Ship it or sink it

Loading embed...
new QaidFeedback({
  endpoint: '/api/feedback',
  positiveIcon: '<span style="font-size: 24px">🚀</span>',
  negativeIcon: '<span style="font-size: 24px">💥</span>',
  colors: {
    positive: "#8b5cf6",
    negative: "#ef4444"
  }
});

Icons plus a styled button

The icon and the button it sits in are set separately, so they compose:

Styled Custom Icons

Icons with custom button styling

Loading embed...
.cyber-btn {
  background: linear-gradient(135deg, #0a0a0a 0%, #1a1a2a 100%);
  border: 2px solid currentColor;
  border-radius: 8px;
  padding: 12px;
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;
}

.cyber-btn::before {
  content: '';
  position: absolute;
  top: 0;
  left: -100%;
  width: 100%;
  height: 100%;
  background: linear-gradient(90deg, transparent, currentColor, transparent);
  opacity: 0.1;
  transition: left 0.5s ease;
}

.cyber-btn:hover::before {
  left: 100%;
}

.cyber-btn:hover {
  transform: scale(1.1);
  box-shadow: 0 0 20px currentColor;
}
new QaidFeedback({
  endpoint: '/api/feedback',
  buttonClass: 'cyber-btn',
  positiveIcon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="24" height="24">
    <circle cx="12" cy="12" r="10"/>
    <path d="M8 14s1.5 2 4 2 4-2 4-2"/>
    <line x1="9" y1="9" x2="9.01" y2="9"/>
    <line x1="15" y1="9" x2="15.01" y2="9"/>
  </svg>`,
  negativeIcon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="24" height="24">
    <circle cx="12" cy="12" r="10"/>
    <path d="M16 16s-1.5-2-4-2-4 2-4 2"/>
    <line x1="9" y1="9" x2="9.01" y2="9"/>
    <line x1="15" y1="9" x2="15.01" y2="9"/>
  </svg>`,
  colors: {
    positive: "#00ffaa",
    negative: "#ff6688"
  }
});

On a script tag

data-positive-icon and data-negative-icon take the same strings:

<script
  src="https://unpkg.com/@qaiddev/thumbs-embed/dist/embed.js"
  data-endpoint="/api/feedback"
  data-positive-icon='<span style="font-size: 24px">👍</span>'
  data-negative-icon='<span style="font-size: 24px">👎</span>'
></script>

Every quote in the SVG has to be escaped to fit inside an HTML attribute, which gets ugly fast. Past a simple path, use the module form.

Drawing your own

Fill with currentColor and the icon inherits the button's text colour, so one CSS rule moves both. Put explicit width and height on the SVG or sizing gets inconsistent between browsers. Then look at it at 36 pixels, which is what buttonSize: 'small' gives you, because detail that reads at 64 disappears at 36.

And make sure the two icons are opposites. A reader deciding which one they mean should not have to think about it.

Next: color customization, text localization, and theme presets.

Back to all articles