The porthole

One script tag, and it looks like your app

The ShipLog widget is a single file of vanilla JavaScript — under 20KB gzipped, no dependencies, no build step and no npm package. It renders in a shadow root, so your CSS cannot leak in and its styles cannot leak out. Every colour is themeable, it can be a popup or a full-height slide-out, and it is on every plan including the free one.

The whole install

Two tags, anywhere in your page. Replace yourapp with your project slug, which you'll find in Settings once you have an account.

<script defer src="https://app.shiplog.ca/widget/shiplog-widget.js"></script>
<script>ShipLog.init({ projectSlug: 'yourapp' })</script>

That is the entire integration. There is no account to connect, no domain to allowlist and no key to keep secret — the slug is public, the same way your board URL is.

Try it on this page

The launcher in the corner is the real widget, not a screenshot — the same file your embed would load. Switch its configuration and it tears down and re-initialises in place, exactly as it would with those options in your own init call.

Submissions here are real and land on a throwaway demo project, so feel free to post something. It is not ShipLog's own board.

Snippets further down this page have a Run button beside Copy — it applies that snippet to the widget above so you can see it without pasting anything into your own app. Those snippets read projectSlug: 'yourapp' because that is the part you replace; Run substitutes this demo project's slug so there is something real to talk to.

The current configuration

This updates as you switch above or hit Run below. Copy it and you get exactly what you're looking at, slug and all.

ShipLog.init({ projectSlug: 'widget-demo-gst2dq' })

Two layouts, four corners

display chooses the shape and position chooses the side. A value the widget doesn't recognise falls back to the popup rather than to no panel at all, so a typo degrades quietly instead of breaking your page.

Popup display: 'popup'
The default. A floating card anchored above the launcher, sized to its content.
Slide-out display: 'slideout'
A full-height panel pinned to whichever side `position` chooses. It overlays the page rather than pushing it, so your layout does not reflow.

Feedback-only mode

If you publish your changelog somewhere else — or not at all — pass feedbackOnly: true and the What's New tab disappears. The widget then skips the changelog request entirely rather than fetching data nothing will render, so it costs your visitors one round trip instead of two.

ShipLog.init({
  projectSlug: 'yourapp',
  feedbackOnly: true
})

Every colour is yours

16 colours, passed as a theme object. Anything you leave out keeps its default, so a dark panel is usually eight or nine values rather than all 16. A theme passed to init overrides the brand colour configured in your project settings, which is what you want when one project is embedded in two differently-skinned places.

<script defer src="https://app.shiplog.ca/widget/shiplog-widget.js"></script>
<script>
  ShipLog.init({
    projectSlug: 'yourapp',
    theme: {
      primaryColor: '#8b5cf6',
      background: '#0f1420',
      surface: '#171d2b',
      text: '#f3f4f6',
      textMuted: '#a6adbb',
      border: '#252c3b',
      inputBorder: '#333c4e',
      divider: '#1e2432'
    }
  })
</script>

All the tokens

Use six-digit hex. Status badges — open, planned, in progress, shipped — keep their own semantic colours and are deliberately not themeable, because a reader who has learned what green means on one board should not have to relearn it on yours.

Token Default Paints
primaryColor #0047B6 buttons, active tab, links
onPrimary #fff text sitting on those buttons
background #fff panel background
surface #fff vote buttons and input fields
text #111827 titles and text you type
textStrong #374151 tab hover, bold changelog text
textMuted #6b7280 descriptions, inactive tabs
textSubtle #9ca3af dates, loading and empty states
textFaint #d1d5db the footer credit
border #e5e7eb panel and vote button borders
inputBorder #d1d5db input and textarea borders
divider #f3f4f6 lines between rows
codeBg #f3f4f6 inline code in changelog entries
accent #ef4444 the unread dot
dangerBg #fef2f2 error message background
dangerText #dc2626 error message text

What reaches your board, and who sees it

The first two of these are project settings rather than init options, because they should hold however many places you've embedded the widget.

Hold submissions for review

Turn on moderation and anything submitted through the widget or your public board waits as pending until you approve it — nothing appears publicly in the meantime, and it isn't counted in vote totals. Rejecting is reversible: the submission is kept, so a mistaken rejection costs you a click rather than the content. The submitter is told their feedback arrived either way, so a held item doesn't read as a failure to them. Writes you make yourself through the API or MCP are not held.

Hide what you've finished

Completed and closed items can be kept out of the widget's list while staying on your public board. Worth turning on once you've shipped a lot: the widget is a small panel, and a long tail of finished requests crowds out the open ones people are there to vote on.

Remove the ShipLog footer

On the Captain plan, pass hideBranding: true and the "Powered by ShipLog" credit goes away. The server decides the entitlement and tells the widget, so passing it on the free plan does nothing rather than half-working — the free plan keeps the footer. Alongside the 50-item soft cap, this is the only difference between the two plans.

Attribute to a signed-in user

Submitting and voting are anonymous by default — votes are deduplicated on a hashed IP, not a login, so nobody has to make an account to be heard. If your app already knows who someone is, tell the widget and their submissions carry it:

ShipLog.identify({ email: 'user@example.com', name: 'Ada L.' })

Questions

Will the widget break my site's CSS?
No, in both directions. The panel renders inside a shadow root, so your stylesheet cannot reach into it and its styles cannot leak out onto your page. That is why the widget looks the same on a Tailwind app, a Bootstrap app and a hand-rolled one. It injects no stylesheet of its own into your document and adds one element to the body.
How big is it, and does it need a build step?
Under 20KB gzipped, and no. It is one file of vanilla JavaScript with no dependencies and no framework — you add a script tag and call ShipLog.init. There is nothing to install, nothing to bundle and no npm package, so it cannot pull a transitive dependency into your app.
Can I make it match a dark theme?
Yes — every colour in the panel is themeable, sixteen of them, passed as a theme object to ShipLog.init. Any colour you leave out keeps its default, so a dark theme is usually eight or nine values. Status badges are the one exception: they carry meaning, so they keep their semantic colours.
Can I use it as a feedback box without the changelog?
Yes. Pass feedbackOnly: true and the What's New tab is hidden, so the widget is a feedback box alone. It also skips the changelog request entirely in that mode rather than fetching data nothing will render.
Can I remove the "Powered by ShipLog" footer?
On the Captain plan ($9/mo per project), by passing hideBranding: true. The server decides the entitlement and tells the widget, so passing it on the free plan does nothing — the free plan keeps the footer. That is the only difference between the plans besides the 50-item soft cap.
Do people need an account to leave feedback?
No. Submitting and voting are anonymous; votes are deduplicated by a hashed IP rather than a login. If your app already knows who someone is you can call ShipLog.identify with their email and name to attribute submissions, but it is optional and the widget works without it.
Can I review submissions before they appear publicly?
Yes. Turn on moderation in project settings and anything submitted through the widget or the public board waits as pending until you approve it. Rejecting is reversible — nothing is destroyed — and the submitter is told their feedback arrived either way. Writes you make yourself through the API or MCP are not held.

Put it in your app

Free plan, every feature, 50 feedback items, no credit card. You'll have a slug to paste into that script tag in about a minute.