1592 Commits

Author SHA1 Message Date
Florian Körner 8bc58b2d96 v10.6.1 v10.6.1 2026-08-18 23:25:14 +02:00
Florian Körner 637f55bc94 fix(core): drop empty wrapper elements from the svg output
An optional component that came up empty left its wrapper behind. In
notionists that wrapper sits inside a mask, and a masked group without
content has no bounding box. AndroidSVG takes the mask size from that
box, so the whole file fails to render and Android gallery apps showed
those avatars as corrupted.

A wrapper is now left out when nothing inside it renders, unless it
carries an id. Same change in all six implementations, the rendered
image does not change.

Refs #553
2026-08-18 23:22:56 +02:00
Florian Körner c05d6a7022 chore: format the ai-assistants guide with prettier 2026-08-16 22:38:32 +02:00
Florian Körner 2897953e97 fix(docs): switch the primevue surface ramp from warm stone to zinc grays 2026-08-16 22:29:14 +02:00
Florian Körner 4e479a505b feat: drop the copy as markdown button
The header keeps the view link, which mobile shows as the Markdown
mark from Simple Icons, and gets a little more room above the title.
2026-08-16 22:05:05 +02:00
Florian Körner 42bea05768 fix: clone the playground options before the batch download renders
The store options are deep-reactive, so nested arrays reach the core as
proxies and its structuredClone rejects them. Every preset that sets a
color array triggered this.

Refs #551
2026-08-16 21:36:50 +02:00
Florian Körner 4f435255ac feat: let the playground export and import its options
Two buttons next to Reset write the style, seed and options to a JSON
file and read one back. Import also takes a bare options block and
checks it against the target style before applying it.

Style, options and seed move from localStorage to sessionStorage, now
that Export covers keeping a look for longer. The old localStorage keys
are cleared. Uploaded custom styles stay in IndexedDB.
2026-08-16 21:33:04 +02:00
Florian Körner 77b9811bd2 feat: drop the styles overview hero
The styles page now starts with the filter card, like the playground.
Both pages keep a screen-reader-only h1 via a new .sr-only utility.
2026-08-16 21:27:22 +02:00
Florian Körner e798d55e6a v10.6.0 v10.6.0 2026-08-16 17:55:14 +02:00
Florian Körner bc1a62d7d8 ci: keep the docs and editor out of the test step too 2026-08-16 15:50:41 +02:00
Florian Körner e029e01ffa feat: add cutouts and line face to the editor 2026-08-16 15:34:02 +02:00
Florian Körner 87561994da feat: offer ready-made presets on every style page 2026-08-16 15:34:02 +02:00
Florian Körner e728fb6626 feat: document the cutouts, line face and patchwork styles 2026-08-16 15:33:44 +02:00
Florian Körner 103d82d50b feat: add a breadcrumb above the docs page title 2026-08-16 15:33:04 +02:00
Florian Körner cdb750fe1a refactor: turn the docs scripts into TypeScript 2026-08-16 15:33:04 +02:00
Florian Körner e6788cfec5 chore: move the CLI, docs and editor to styles 10.5.0 2026-08-16 11:17:17 +02:00
Florian Körner fcf1bb96e8 fix: render the license deed quotes from data
Two blocks of literal markup in one page chunk compile to two
createStaticVNode calls, and VitePress merges them into a single call
with the wrong node count while it strips the static content out of the
lean chunk it hydrates with. The licenses page died on that walk, which
left all 52 avatars without a src and the copy buttons without handlers.

The deed paragraphs now live in the group data and render through v-for,
so the chunk emits no static block for the compaction to hit. The quoted
wording is unchanged.
2026-08-14 14:34:19 +02:00
Florian Körner e8b4c8c053 style: reflow the docs files that eslint and prettier flagged
The comment above StyleRow.credit opened with its text on the same line
as the /**, which multiline-comment-style does not count as JSDoc, so
the rule asked for line comments instead. It now starts on its own line
like the other doc blocks in the theme, which puts it back under the
checkJSDoc exception.

The rest is prettier. Four files from the licenses page work carried
hand-set line breaks. src/python/core/.pytest_cache/README.md is
generated and gitignored, but the prettier glob reaches into it anyway,
so the cache directory joins the other build output in .prettierignore.
2026-08-14 14:05:09 +02:00
Florian Körner 1d5f118943 fix: assemble the licenses page mirror from the style metadata
The Markdown mirror of /licenses/ carried only the hand-written intro:
like the style pages, the page itself is a component mount, but unlike
them it had no generator, so assistants fetching /licenses/index.md got
nine lines and none of the license content.

The new renderLicensesPage builds the mirror the same way the page is
built: one section per license bucket from normalizeLicense, the
remix/port wording from attributionPrefix, the per-style credits and
links from the style metadata, and the MIT notices, including both
copyright lines, from the definition and the repository LICENSE. Since
page and mirror read the same sources, they cannot drift apart.
2026-08-14 14:00:08 +02:00
Florian Körner 54b8926fa7 feat: redesign the licenses page
The licenses page was a stack of 52 bare tables that never showed the
license texts themselves. It now reads as one section per license, each
introduced by a short sentence and, for CC0 1.0 and CC BY 4.0, the
complete deed summary quoted verbatim from Creative Commons, with the
deeds' own sub-headings and bold terms. The cite lines name the source
and link CC's policies page, which dedicates the deed texts to the
public domain under CC0. A footnote marks the page as orientation, not
legal advice.

Each section lists its styles as card rows: avatar, name, and the
source credit built from attributionKind. The MIT section and the new
Software section surface the copyright lines and ship the full license
texts behind an expander with a copy button; the software text comes
from the repository's LICENSE file via a new config module, so the
page cannot drift from the file it cites.

Along the way, shared pieces moved to where they belong. The ghost
copy-button chrome that StyleInfo and this page both use became the
global ui-copy-button class. normalizeLicense now returns the new
LicenseBucket union and the sections are typed against it, so a new
bucket fails the build instead of silently dropping styles from the
page. The "Remix of"/"Based on" prefix moved into attributionPrefix
next to attributionKind, shared with the OG cards.
2026-08-14 13:43:32 +02:00
Florian Körner 354137375d feat: redesign the details section of the style pages
The Naming, Source, and Popularity blocks were headerless PrimeVue
DataTables that squeezed code blocks, links, and a sparkline into
uniform rows. The section now reads as a fact sheet: the URLs and
Source cards render label/value rows with hairline separators, and
the URL rows get a copy button that appears on hover. The rows for
the definition import and the CLI command are gone; the usage tabs
above already cover both, which shrank the Naming card to HTTP-API
and Definition under the title URLs.

Popularity becomes a small stat band: tiles for rank, websites, and
trend, the week label in the card header, and a zero-anchored area
chart of the last 12 weeks that bleeds to the card border, drawn in
the same visual language as the statistics page.

Along the way, shared pieces moved to where they belong. A new
UiCopyButton owns the clipboard affordance for UiCode and the URL
rows. chartGeometry.ts holds the zero-anchored series scaling that
the sparkline, the home page banner cards, and the new chart all
use. UiCard gained a header-actions slot like UiWindow, so no
consumer has to mirror its title styles. AppStatsTrendBadge renders
the muted dot for missing growth data itself, and the small mono
field label became a global ui-eyebrow class.
2026-08-14 11:49:47 +02:00
Florian Körner 9cd9890471 feat: redesign the statistics around weekly style rankings
The statistics page now builds on the weekly unique-referer aggregates
of the stats API. A trending row shows the styles gaining the most
websites, comparing the past four weeks with the four before; a style
that grew from zero gets a New badge instead of a meaningless
percentage. Below it, a searchable table ranks every style with its
weekly website count, share, trend, and a zero-anchored sparkline.
Rows navigate to the style pages, columns sort on click, and weeks
with lost logs stay out of both the charts and the trends.

Package downloads get one tab per registry the ports ship to: npm,
Packagist, PyPI, and crates.io, all drawn on one shared week axis so
switching tabs never changes the time range. The version and format
charts read as the share of websites active in each week.

The ranking is derived once in useStyleRankings and shared everywhere:
each style page shows rank, websites, trend, and sparkline under
Details, and the styles overview can sort by Most used or Trending.
2026-08-13 22:59:03 +02:00
Florian Körner cb13875d9d fix: keep the llms dev middleware away from Vite module requests
Since the Markdown mirrors shipped, vitepress dev rendered the 404 page
for every route that has one: VitePress loads its page modules under
the same .md URLs, and the middleware answered them with raw Markdown
instead of letting Vite compile the page. Those module requests always
carry a query (?import, ?t=...), so the middleware now serves the
mirror only for the bare URL and passes everything else through.
2026-08-13 22:58:10 +02:00
Florian Körner fc85049735 ci: drop the shallow fetch that broke the latest dist-tag check
A --depth=1 fetch cuts the branch history at its tip, so the merge-base
ancestor check failed for any tag that was no longer the tip of 10.x.
That is how 10.5.0 ended up on npm as v10-lts instead of latest; the
dist-tags have been corrected by hand.
2026-08-12 22:28:10 +02:00
Florian Körner 3f2b396fcb feat: serve the docs to AI assistants as llms.txt and Markdown mirrors
Language models carry DiceBear knowledge that mostly predates version
10, and the old HTTP API versions still answer, so outdated code looks
correct while it silently does the wrong thing. The build now writes an
llms.txt index with the current package versions, an llms-full.txt with
the complete prose, and a Markdown mirror next to every page, in both
the /page/index.md and the /page.md spelling. A dev middleware serves
the same files locally, each page links its mirror through a copy
button, a View-as-Markdown link and a rel=alternate hint, and a new
guide carries a rules block for AGENTS.md files. robots.txt points
crawlers at the machine-readable files; a per-bot section would be
redundant next to the wildcard rule, so there is none.

Style pages are almost entirely component mounts, so their mirrors are
assembled from the style definitions instead: license, an option table,
and install and usage snippets in all six languages. The snippets come
from the new usageSnippets.ts, which StyleUsage.vue reads as well, so
the tabs and the mirrors cannot drift apart and an API change is
spelled once. The per-style constants it derives match the generated
constants in the styles packages for all 52 styles.

The count tokens now also fill in page bodies through a markdown-it
rule, replacing the one Vue interpolation that would have shipped as
literal text, and prepareLlms fails the build when a mirror still
contains a construct the cleaning does not handle. The site notice
disallow in robots.txt pointed at /legal/site-notice/ while the page
lives under /legal/legal-notice/, so it never matched; the rule now
covers /legal/.

The mirrors and llms files depend on two Bunny edge rules: text/plain
with a UTF-8 charset, because the pull zone sends no charset and
Firefox downloads text/markdown instead of showing it, and
X-Robots-Tag: noindex, because every page links its mirror and the
copies would otherwise compete with the HTML pages as duplicate
content. Assistants are unaffected by noindex: training crawlers and
user-triggered fetchers follow robots.txt, not index directives. The
deploy verifies both rules after the purge and fails when one is
missing.
2026-08-12 00:00:32 +02:00
Florian Körner 91d8cab4ef docs: add the star and license badges to the package READMEs
The badges served from dicebear.com now also sit in the READMEs of the
eight published packages, right below the heading, so they show up
wherever a registry renders the file: npm, Packagist, PyPI, crates.io,
pkg.go.dev, and pub.dev.

The version badge stays in the root README. Every registry prints the
version next to the package name anyway, and a single badge would name
the core version on the CLI page too. The license link is absolute,
because a relative ./LICENSE resolves nowhere outside GitHub.
2026-08-09 23:22:37 +02:00
Florian Körner deb9d60ae6 ci: rebuild the website weekly and abort on a failed legal download
The star badge and the star counts in the comparison table are baked in
at build time, so they only change when the site is rebuilt. Deploy
Website now also runs Mondays at 04:17 UTC. Scheduled runs only ever use
the default branch, which leaves the 9.x site to manual dispatch, and a
concurrency group per branch keeps a scheduled run from cancelling a
manual one mid-upload.

download-legal-pages.sh redirected curl into the tracked page, which the
shell truncated before curl even ran. That page is a stub with nothing
but frontmatter, so a failed download would have deployed an empty legal
notice. The response goes to a temporary file now and is moved into
place only once it arrives non-empty, leaving the checkout untouched on
failure. An empty PAT is reported as such instead of surfacing as a 401,
and --retry covers timeouts and 5xx while an authentication failure is
not retried.
2026-08-09 23:14:59 +02:00
Florian Körner 812dc501ba feat: ask for a GitHub star after users copy or download an avatar
The star ask now sits where the user has just got what they came for:
the playground copy and download dialogs, the editor download dialog,
and a section at the end of the README. The playground button reports a
Umami event with the source, so copy and download can be compared.

The new /support page collects the ways to help and is the npm funding
target of core, converter, and the CLI, which means `npm fund` prints a
URL that can be repointed later without republishing the packages.

The README badges are generated during the docs build and served from
dicebear.com rather than shields.io, so no third party sees the IP of
everyone who opens the README on npmjs.com. Their text is pinned with
textLength because the SVG renders in whatever font the reader's system
substitutes for Verdana. The star count is the value fetched at build
time and therefore lags between website deploys.

fetchGitHubStars sends GITHUB_TOKEN when it is set and logs a failed
response instead of dropping it silently. Unauthenticated api.github.com
allows 60 requests per hour and IP, which shared runners exhaust; the
deploy workflow now passes the token. Without a count the star badge
falls back to a single segment instead of breaking the image.
2026-08-09 23:08:35 +02:00
Florian Körner 1eab65b23c fix(core): drop a redundant array_values call in the PHP descriptor
Color::notEqualTo() already returns a list<string>, so wrapping it
reindexes nothing. PHPStan rejects the call under the arrayValues.list
rule.
2026-08-09 23:00:56 +02:00
Florian Körner c331bbe053 fix(editor): keep the style's color constraints when shuffling
Shuffle drew every color on its own, so nothing stopped it from giving
the shape the background color or putting black eyes on a dark shape.
In thumbs the shape vanished in about one of five shuffles, and eyes
and mouth took the worse of black and white about half the time. clay,
critters, micah, voxel-art, and voxel-bot were affected too.

Colors now resolve in dependency order, and the constraints from the
definition are applied with Color.sortByContrast and
Color.filterNotEqualTo from @dicebear/core, the same code the renderer
runs. Across 5000 draws per style, all 32 editor styles now satisfy
every contrastTo and notEqualTo constraint.
2026-08-09 22:43:41 +02:00
Florian Körner 8ce85ab046 feat(core): expose notEqualTo in the options descriptor
Color fields already carried contrastTo. They now also carry
notEqualTo, the list of color groups a group must differ from. Tooling
that picks colors itself needs both: writing one explicit color per
group leaves the renderer nothing to sort or filter, so the constraint
has to be applied before the option is set.

Implemented in all six cores (JavaScript, PHP, Python, Rust, Go, and
Dart), pinned by the descriptor parity fixture for thumbs and a unit
case per port, and documented in the guide on accessing all available
options.
2026-08-09 22:42:49 +02:00
Florian Körner ac2d79c7df Update dependencies 2026-08-09 20:52:18 +02:00
Florian Körner 7d54afd83a v10.5.0 v10.5.0 2026-08-09 20:51:15 +02:00
Florian Körner d378430aac feat(docs): two-column layout for the bundle size estimator
The style picker and the package toggles sit on the left, the sticky
calculation column on the right, summarized as one Library row plus the
selected styles. The summary's compact styles now key on the column
width through a container query instead of the viewport. Both tool
pages render as layout: page, since layout: doc capped the tools at the
doc column width; the contrast tool keeps its own small container. In
dark mode the package code chips get a visible background, the same
treatment the style slugs already had.
2026-08-09 20:46:40 +02:00
Florian Körner fd65d1133f chore(docs): update vitepress to 2.0.0-alpha.19 2026-08-09 20:31:01 +02:00
Florian Körner b797680d19 feat(docs): list the option groups in the style page outline
The group titles are now real h3 headings and the option cards move to
h4, so opening the style pages' outline to level 3 shows the groups
without flooding the aside with every option. The aside only scans the
DOM when VitePress announces new content, which happens before the
async style load, so StyleOptions replays the announcement once the
groups settle and again when the search filter changes. The replay
reaches into vitepress/dist/client/app/utils.js, which is not public
API; the comment at the import marks the spot to revisit on a VitePress
update.
2026-08-09 20:20:10 +02:00
Florian Körner c55fe7784b feat: put an avatar hero graphic at the top of the README
scripts/generate-readme-hero.mjs renders 24 curated tiles in the
release-graphic look: staggered rounded tiles on the blue gradient,
CC0-licensed styles only (the generator enforces this), friendly eyes
and mouth variants pinned wherever a style offers them, pastel
backgrounds spread across the grid, and six animated tiles distributed
two per row. The README references the SVG through www.dicebear.com,
like the logo, so it appears once the docs deploy.
2026-08-09 20:11:02 +02:00
Florian Körner 2c222ce881 feat(docs): serve the tools pages without a sidebar
The overview page already opted out. Removing the /tools/ sidebar
mapping and its two-entry config clears it from the contrast picker
and the bundle size estimator as well.
2026-08-09 20:11:02 +02:00
Florian Körner 3c90fa75da style: satisfy the format and fixture checks
The embedded Dart web fixtures were generated before the final parity
fixture round and never went through dart format, which failed both the
format job and the up-to-date check. The animated-avatars front matter
needed a Prettier rewrap.
2026-08-09 19:30:42 +02:00
Florian Körner d2209cb36a docs(changelog): put the ColorOrder entries in active voice
The Added entry closed on a stack of three passive clauses; it now names
who does what in two sentences. The Deprecated entry loses its passive
opener the same way.
2026-08-09 19:23:12 +02:00
Florian Körner a8fadb256b feat(docs): surface the *ColorOrder option on style pages and in the playground
The style pages group color options by an explicit suffix list, so the
new option fell out of the color cards; it now sits with Fill, Angle,
and Stops, with a preview that runs three palette colors through a
linear gradient to make random and fixed distinguishable. The preview
renders through the local library, because the deployed HTTP API
rejects the option until it ships a core that knows it.

The playground gains an Order select per color, shown like Angle and
Stops only for gradient fills. Switching back to solid clears a set
value: the core would still apply it there (fixed pins the first
color), and a hidden field must not change the avatar.
2026-08-09 19:16:18 +02:00
Florian Körner 615512c3d2 feat(core): add the *ColorOrder render option
Colors passed via *Color can now keep their order. With `fixed`,
gradient fills apply them as color stops from first to last, solid
fills always use the first color, and the stop count defaults to the
number of given colors. Without user-supplied colors, `fixed` only
skips the shuffle: the style palette stays deduplicated, sorted, and
constraint-driven, so style defaults keep working and definition
constraints such as contrastTo still apply. `random` stays the
default and existing avatars render byte-identically. Requested in
https://github.com/orgs/dicebear/discussions/549 for gradients with a
fixed color sequence, such as flag colors.

Implemented in all six cores (JavaScript, PHP, Python, Rust, Go, and
Dart), validated by @dicebear/schema 1.4.0, pinned by two new parity
fixture cases per style, and documented in the core options guide and
the implementation spec. DiceBear 11 will drop the palette sort in the
fixed case and take the definition order; the changelog and the sort
sites in every port carry the deprecation note.
2026-08-09 18:59:33 +02:00
Florian Körner dcfc5fc834 docs(changelog): lead the styles bump with the CLI
Users get @dicebear/styles through the CLI package; the docs and the
editor only display the styles.
2026-08-09 18:58:41 +02:00
Florian Körner 3c7a09f04e test(parity): vendor the current styles build into the fixtures
The vendored style sources were last copied from the 10.3.0-rc.1 build of
@dicebear/styles. The current build serializes the same definitions
differently (key order, `.5` instead of `0.5`), so the initials and
thumbs avatar fixtures change their bytes with it. Rendering behavior is
unchanged; the avatar diff is the style data passing through verbatim.
2026-08-09 18:56:55 +02:00
Florian Körner 030a98a552 feat(editor): carry every character style
Adds the eight character styles the editor was missing: clay,
critters, moods, pixelbot, sprouts, thumbs, voxel-art and voxel-bot.
The style list now matches the docs' Characters category exactly, and
the new option keys are translated in English, German and Portuguese.

The animation option is filtered out of the schema options: the
editor's export writes static files. Every animated variant carries
weight 0, so an avatar without an explicit animationVariant never
animates.
2026-08-09 16:25:55 +02:00
Florian Körner 850a3ec398 feat: adopt @dicebear/styles 10.4.0 with the two voxel styles
The release adds voxel-art and voxel-bot and takes the collection
from 50 to 52 styles. The docs register both (category map,
definition imports, sidebar badges and icon list, regenerated preview
seeds) and get a page for each.

The animated-avatars description now fills its count from a new
%ANIMATED_STYLE_COUNT% token at build time; the hardcoded 15 it
replaces had already gone stale. The animated grid moves from four to
six columns so its 18 tiles keep even rows.
2026-08-09 16:24:54 +02:00
Florian Körner ddeca70c67 feat(docs): put four v10 styles on the default social card
The default Open Graph card still showed the v9 mix, and two of its
tiles (thumbs, shapes) nearly vanished against the card gradient.
lorelei stays as the anchor; critters, clay, pixelbot and planets take
the other four slots, all CC0 as the missing credit line requires.
Also updates the stale style counts in the TILE_BASE comment.
2026-08-09 14:58:25 +02:00
Florian Körner ecdb3f793d feat(docs): give the footer a dedicated tools column
Replace the single link to the tools overview page with a column that
lists every tool. Playground and Editor move there from the Explore
column. The link columns now sit on an equal-width grid, so columns
that wrap on narrow viewports line up with the ones above them.
2026-08-09 14:25:49 +02:00
Florian Körner 435070546b perf(converter): drop the XML parser from the browser bundle
The browser build parsed SVGs with fast-xml-parser for exactly two
jobs: writing width/height onto the root element before canvas
rasterization, and mirroring mask-type style declarations onto the
presentation attribute. Bundled, that stack (fast-xml-parser,
fast-xml-builder, entity handling and friends) came to 76 kB of the
79 kB bundle. The browser has native XML machinery, so both helpers
now run on DOMParser and XMLSerializer in a new utils/svg-browser.ts,
and a browser bundle of the package shrinks from 26 kB to 1.4 kB
gzipped.

The Node build keeps the fast-xml-parser stack; Node has no native
XML APIs and prepareForResvg needs the full round trip. The logic
both sides share moved into dependency-free modules: sanitizeSize
into utils/size.ts, the mask-type declaration parsing into
utils/mask-type.ts. The node-side ensureSize lost its last caller
with the switch and is gone; its tests now target prepareForResvg,
which wraps the same rebuild.

Two edges change with the parser. A malformed SVG fails with a clear
error in the browser instead of a parser-specific one, and when
normalizeMaskType rewrites a document, empty elements come back
self-closing. Text nodes, CDATA sections, and comments before the
root element survive byte-exact; new jsdom-based tests pin that down,
including the text regression from 10.3.2.
2026-08-09 13:51:55 +02:00
Florian Körner 3722bc8830 fix(docs): measure real bundle sizes in the bundle size estimator
The estimator gzipped every published lib file on its own, without
minification, and summed the results. That is not what a bundler ships.
Core came out at 58 kB although a minified esbuild bundle gzips to
26 kB; every file paid its own gzip header and the readable tsc output
was never minified. The converter showed 8 kB although its browser
build weighs 26 kB, because the per-package walk never counted the XML
dependencies (fast-xml-parser and friends) that a bundler pulls in.

avatarStyleSizes.ts now builds one minified esbuild bundle per package
at docs build time and gzips that output as a single stream. With
platform set to browser, esbuild resolves the converter's exports map
to the browser entry instead of the node one. The style numbers keep
the old method; a style is a single JSON file, where both effects are
irrelevant, and spot checks matched to within 0.1 kB.

esbuild moves into the docs devDependencies instead of relying on
hoisting through vite. The converter hint in the tool claimed PDF
output; the package converts to PNG, JPEG, WebP, and AVIF.
2026-08-09 13:34:24 +02:00