PageSourceSearch

https://sveltiacms.app/assets/en_showcase.md.Cxoc_V7W.lean.js

js sveltiacms.app collected 2026-10-03 22:48:44 UTC 26,209 bytes, 405 lines download raw bytes

1import { Dt as watch, Et as useTemplateRef, Ht as normalizeClass, J as vModelSelect, Lt as ref, Q as Fragment, Vt as unref, Wt as toDisplayString, Y as vModelText, bt as renderList, et as computed, ht as onMounted, it as createElementBlock, jt as withDirectives, lt as defineComponent, mt as onBeforeUnmount, n as _plugin_vue_export_helper_default, ot as createTextVNode, pt as nextTick, rt as createCommentVNode, st as createVNode, tt as createBaseVNode, vt as openBlock } from "./chunks/framework.BcTgqkOM.js";
2import { t as sites } from "./chunks/showcase-sites.cc4oV5RS.js";
3//#region data/utils.js
4/**
5* Generate a safe filename slug from a URL
6* @param {string} url - The URL to convert
7* @returns {string} - The slugified filename
8*/
9function getSafeSlug(url) {
10	const { hostname, pathname } = new URL(url);
11	return `${hostname}${pathname}`.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
12}
13//#endregion
14//#region .vitepress/theme/components/ShowcaseList.vue?vue&type=script&setup=true&lang.ts
15var _hoisted_1$1 = { class: "showcase-container" };
16var _hoisted_2 = { class: "filters-wrapper" };
17var _hoisted_3 = { class: "filters" };
18var _hoisted_4 = { class: "filter-group" };
19var _hoisted_5 = { for: "category-filter" };
20var _hoisted_6 = { value: "all" };
21var _hoisted_7 = ["value"];
22var _hoisted_8 = { class: "filter-group" };
23var _hoisted_9 = { for: "country-filter" };
24var _hoisted_10 = { value: "all" };
25var _hoisted_11 = ["value"];
26var _hoisted_12 = { class: "filter-group" };
27var _hoisted_13 = { for: "feature-filter" };
28var _hoisted_14 = { value: "all" };
29var _hoisted_15 = ["value"];
30var _hoisted_16 = { class: "filter-group" };
31var _hoisted_17 = { for: "framework-filter" };
32var _hoisted_18 = { value: "all" };
33var _hoisted_19 = ["value"];
34var _hoisted_20 = { class: "filter-group" };
35var _hoisted_21 = { for: "cms-filter" };
36var _hoisted_22 = { value: "all" };
37var _hoisted_23 = ["value"];
38var _hoisted_24 = { class: "filter-group" };
39var _hoisted_25 = { for: "search-filter" };
40var _hoisted_26 = ["placeholder"];
41var _hoisted_27 = { class: "showcase-list" };
42var _hoisted_28 = {
43	key: 0,
44	class: "no-results"
45};
46var _hoisted_29 = {
47	key: 0,
48	class: "featured-badge"
49};
50var _hoisted_30 = ["href"];
51var _hoisted_31 = ["src", "alt"];
52var _hoisted_32 = ["href"];
53var _hoisted_33 = { key: 0 };
54var _hoisted_34 = { key: 1 };
55var _hoisted_35 = { key: 0 };
56var _hoisted_36 = ["href"];
57var PAGE_SIZE = 48;
58//#endregion
59//#region .vitepress/theme/components/ShowcaseList.vue
60var ShowcaseList_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
61	__name: "ShowcaseList",
62	props: {
63		sites: {},
64		labels: {}
65	},
66	setup(__props) {
67		const props = __props;
68		/**
69		* Generic sorting utility that sorts items by their display labels.
70		* @param items - Array of items to sort
71		* @param labelMap - Map of item keys to their display labels
72		* @returns Sorted array
73		*/
74		const sortByLabel = (items, labelMap) => {
75			return items.sort((a, b) => (labelMap[a] || a).localeCompare(labelMap[b] || b));
76		};
77		/**
78		* Sets a URL parameter if the value differs from the default.
79		* @param params - URLSearchParams object to update
80		* @param key - Parameter key
81		* @param value - Current value
82		* @param defaultValue - Default/unset value
83		* @param mapValue - Optional function to transform the value before setting
84		*/
85		const setUrlParam = (params, key, value, defaultValue, mapValue) => {
86			if (value !== defaultValue) params.set(key, mapValue ? mapValue(value) : value);
87		};
88		/**
89		* Number of sites to render at a time. Divisible by 2, 3 and 4 so that full rows are filled
90		* regardless of the number of grid columns.
91		*/
92		const selectedCategory = ref("all");
93		const selectedFramework = ref("all");
94		const selectedMigratedFrom = ref("all");
95		const selectedFeature = ref("all");
96		const selectedCountry = ref("all");
97		const searchQuery = ref("");
98		const updateUrlParams = () => {
99			const params = new URLSearchParams();
100			if (searchQuery.value) params.set("q", searchQuery.value);
101			setUrlParam(params, "category", selectedCategory.value, "all");
102			setUrlParam(params, "framework", selectedFramework.value, "all", (v) => v === null ? "none" : v);
103			setUrlParam(params, "migrated-from", selectedMigratedFrom.value, "all");
104			setUrlParam(params, "feature", selectedFeature.value, "all");
105			setUrlParam(params, "country", selectedCountry.value, "all", (v) => v === null ? "global" : v);
106			const query = params.toString();
107			const newUrl = query ? `${window.location.pathname}?${query}` : window.location.pathname;
108			window.history.replaceState({}, "", newUrl);
109		};
110		onMounted(() => {
111			const params = new URLSearchParams(window.location.search);
112			if (params.has("q")) searchQuery.value = params.get("q") || "";
113			if (params.has("category")) {
114				const category = params.get("category");
115				if (category === "all" || Object.keys(props.labels.category).includes(category)) selectedCategory.value = category === "all" ? "all" : category;
116			}
117			if (params.has("framework")) {
118				const framework = params.get("framework");
119				selectedFramework.value = framework === "none" ? null : framework || "all";
120			}
121			if (params.has("migrated-from")) {
122				const cms = params.get("migrated-from");
123				selectedMigratedFrom.value = cms || "all";
124			}
125			if (params.has("feature")) {
126				const feature = params.get("feature");
127				selectedFeature.value = feature || "all";
128			}
129			if (params.has("country")) {
130				const country = params.get("country");
131				selectedCountry.value = country === "global" ? null : country || "all";
132			}
133		});
134		watch([
135			searchQuery,
136			selectedCategory,
137			selectedFramework,
138			selectedMigratedFrom,
139			selectedFeature,
140			selectedCountry
141		], () => {
142			updateUrlParams();
143		});
144		const frameworks = computed(() => {
145			const uniqueFrameworks = new Set(props.sites.map((site) => site.framework).filter((fw) => !!fw));
146			const last = ["vanilla", "custom"];
147			const lastInSet = last.filter((fw) => uniqueFrameworks.has(fw));
148			last.forEach((fw) => uniqueFrameworks.delete(fw));
149			return [...sortByLabel(Array.from(uniqueFrameworks), props.labels.framework), ...lastInSet];
150		});
151		const migratedFromOptions = computed(() => {
152			const unique = new Set(props.sites.flatMap((site) => site.migrated_from));
153			return sortByLabel(Array.from(unique), props.labels.cms);
154		});
155		const features = computed(() => {
156			const uniqueFeatures = /* @__PURE__ */ new Set();
157			props.sites.forEach((site) => {
158				site.features?.forEach((feature) => uniqueFeatures.add(feature));
159			});
160			return sortByLabel(Array.from(uniqueFeatures), props.labels.feature);
161		});
162		const countries = computed(() => {
163			const uniqueCountries = new Set(props.sites.map((site) => site.country).filter((country) => !!country));
164			return [...Array.from(uniqueCountries).sort((a, b) => getCountryName(a).localeCompare(getCountryName(b))), null];
165		});
166		const categoriesSorted = computed(() => {
167			return sortByLabel(Object.keys(props.labels.category), props.labels.category);
168		});
169		const filteredSites = computed(() => {
170			const rawQuery = searchQuery.value.trim();
171			const searchRegex = rawQuery ? new RegExp(`\\b${rawQuery.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`, "i") : null;
172			const filtered = props.sites.filter((site) => {
173				if (searchRegex) {
174					if (!searchRegex.test(site.name) && !searchRegex.test(site.description)) return false;
175				}
176				if (selectedCategory.value !== "all" && !site.categories.includes(selectedCategory.value)) return false;
177				if (selectedFramework.value !== "all" && site.framework !== selectedFramework.value) return false;
178				if (selectedMigratedFrom.value !== "all" && !site.migrated_from.includes(selectedMigratedFrom.value)) return false;
179				if (selectedFeature.value !== "all" && !site.features?.includes(selectedFeature.value)) return false;
180				if (selectedCountry.value !== "all" && site.country !== selectedCountry.value) return false;
181				return true;
182			});
183			const featured = filtered.filter((site) => site.featured);
184			const rest = filtered.filter((site) => !site.featured).reverse();
185			return [...featured, ...rest];
186		});
187		const visibleCount = ref(PAGE_SIZE);
188		const visibleSites = computed(() => filteredSites.value.slice(0, visibleCount.value));
189		const hasMore = computed(() => visibleCount.value < filteredSites.value.length);
190		const loadMoreButton = useTemplateRef("load-more");
191		let observer;
192		const loadMore = () => {
193			visibleCount.value += PAGE_SIZE;
194		};
195		/**
196		* (Re)start observing the Load More button. Observing an element always triggers the callback
197		* once, so this also loads the next batch when the button is still in view after an update, e.g.
198		* on a tall screen.
199		*/
200		const observeLoadMoreButton = async () => {
201			await nextTick();
202			observer?.disconnect();
203			if (loadMoreButton.value) observer?.observe(loadMoreButton.value);
204		};
205		onMounted(() => {
206			observer = new IntersectionObserver(([entry]) => {
207				if (entry?.isIntersecting) loadMore();
208			}, { rootMargin: "0px 0px 1000px 0px" });
209			observeLoadMoreButton();
210		});
211		onBeforeUnmount(() => {
212			observer?.disconnect();
213		});
214		watch(filteredSites, () => {
215			visibleCount.value = PAGE_SIZE;
216		});
217		watch(visibleCount, () => {
218			observeLoadMoreButton();
219		});
220		/**
221		* Generates a safe image path for a given URL by creating a slug and appending the .webp extension.
222		* @param url - The URL of the site for which to generate the image path.
223		* @returns The generated image path in the format /images/showcase/{slug}.webp
224		*/
225		const getImagePath = (url) => `/images/showcase/${getSafeSlug(url)}.webp`;
226		let countryDisplayNames;
227		/**
228		* Converts a country code (e.g., "US", "FR") to its display name (e.g., "United States", "France").
229		* If the conversion fails, it returns the original country code.
230		* @param countryCode - The ISO 3166-1 alpha-2 country code to convert.
231		* @returns The display name of the country or the original country code if conversion fails.
232		*/
233		const getCountryName = (countryCode) => {
234			try {
235				countryDisplayNames ??= new Intl.DisplayNames(["en"], { type: "region" });
236				return countryDisplayNames.of(countryCode) || countryCode;
237			} catch {
238				return countryCode;
239			}
240		};
241		/**
242		* Resolves keys to their display labels and sorts them alphabetically.
243		* @param keys - Array of category/feature keys
244		* @param labelMap - Map of keys to their display labels
245		* @returns Sorted display labels
246		*/
247		const getSortedLabels = (keys, labelMap) => keys.map((key) => labelMap[key] || key).sort((a, b) => a.localeCompare(b));
248		return (_ctx, _cache) => {
249			return openBlock(), createElementBlock("div", _hoisted_1$1, [
250				createBaseVNode("div", _hoisted_2, [createBaseVNode("div", _hoisted_3, [
251					createBaseVNode("div", _hoisted_4, [
252						createBaseVNode("label", _hoisted_5, toDisplayString(__props.labels.filters.category.label), 1),
253						withDirectives(createBaseVNode("select", {
254							id: "category-filter",
255							"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => selectedCategory.value = $event)
256						}, [createBaseVNode("option", _hoisted_6, toDisplayString(__props.labels.filters.category.all), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(categoriesSorted.value, (category) => {
257							return openBlock(), createElementBlock("option", {
258								key: category,
259								value: category
260							}, toDisplayString(__props.labels.category[category]), 9, _hoisted_7);
261						}), 128))], 512), [[vModelSelect, selectedCategory.value]]),
262						_cache[6] || (_cache[6] = createBaseVNode("span", { class: "vpi-chevron-down text-icon" }, null, -1))
263					]),
264					createBaseVNode("div", _hoisted_8, [
265						createBaseVNode("label", _hoisted_9, toDisplayString(__props.labels.filters.country.label), 1),
266						withDirectives(createBaseVNode("select", {
267							id: "country-filter",
268							"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => selectedCountry.value = $event)
269						}, [createBaseVNode("option", _hoisted_10, toDisplayString(__props.labels.filters.country.all), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(countries.value, (country) => {
270							return openBlock(), createElementBlock("option", {
271								key: country ?? "global",
272								value: country
273							}, toDisplayString(country ? getCountryName(country) : __props.labels.filters.country.global), 9, _hoisted_11);
274						}), 128))], 512), [[vModelSelect, selectedCountry.value]]),
275						_cache[7] || (_cache[7] = createBaseVNode("span", { class: "vpi-chevron-down text-icon" }, null, -1))
276					]),
277					createBaseVNode("div", _hoisted_12, [
278						createBaseVNode("label", _hoisted_13, toDisplayString(__props.labels.filters.feature.label), 1),
279						withDirectives(createBaseVNode("select", {
280							id: "feature-filter",
281							"onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => selectedFeature.value = $event)
282						}, [createBaseVNode("option", _hoisted_14, toDisplayString(__props.labels.filters.feature.all), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(features.value, (feature) => {
283							return openBlock(), createElementBlock("option", {
284								key: feature,
285								value: feature
286							}, toDisplayString(__props.labels.feature[feature] || feature), 9, _hoisted_15);
287						}), 128))], 512), [[vModelSelect, selectedFeature.value]]),
288						_cache[8] || (_cache[8] = createBaseVNode("span", { class: "vpi-chevron-down text-icon" }, null, -1))
289					]),
290					createBaseVNode("div", _hoisted_16, [
291						createBaseVNode("label", _hoisted_17, toDisplayString(__props.labels.filters.framework.label), 1),
292						withDirectives(createBaseVNode("select", {
293							id: "framework-filter",
294							"onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => selectedFramework.value = $event)
295						}, [createBaseVNode("option", _hoisted_18, toDisplayString(__props.labels.filters.framework.all), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(frameworks.value, (framework) => {
296							return openBlock(), createElementBlock("option", {
297								key: framework ?? "none",
298								value: framework
299							}, toDisplayString(framework ? __props.labels.framework[framework] : __props.labels.framework.none), 9, _hoisted_19);
300						}), 128))], 512), [[vModelSelect, selectedFramework.value]]),
301						_cache[9] || (_cache[9] = createBaseVNode("span", { class: "vpi-chevron-down text-icon" }, null, -1))
302					]),
303					createBaseVNode("div", _hoisted_20, [
304						createBaseVNode("label", _hoisted_21, toDisplayString(__props.labels.filters.cms.label), 1),
305						withDirectives(createBaseVNode("select", {
306							id: "cms-filter",
307							"onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => selectedMigratedFrom.value = $event)
308						}, [createBaseVNode("option", _hoisted_22, toDisplayString(__props.labels.filters.cms.all), 1), (openBlock(true), createElementBlock(Fragment, null, renderList(migratedFromOptions.value, (cms) => {
309							return openBlock(), createElementBlock("option", {
310								key: cms,
311								value: cms
312							}, toDisplayString(__props.labels.cms[cms] || cms), 9, _hoisted_23);
313						}), 128))], 512), [[vModelSelect, selectedMigratedFrom.value]]),
314						_cache[10] || (_cache[10] = createBaseVNode("span", { class: "vpi-chevron-down text-icon" }, null, -1))
315					]),
316					createBaseVNode("div", _hoisted_24, [createBaseVNode("label", _hoisted_25, toDisplayString(__props.labels.filters.search.label), 1), withDirectives(createBaseVNode("input", {
317						id: "search-filter",
318						"onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => searchQuery.value = $event),
319						type: "search",
320						placeholder: __props.labels.filters.search.placeholder
321					}, null, 8, _hoisted_26), [[vModelText, searchQuery.value]])])
322				])]),
323				createBaseVNode("div", _hoisted_27, [filteredSites.value.length === 0 ? (openBlock(), createElementBlock("p", _hoisted_28, toDisplayString(__props.labels.no_results), 1)) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(visibleSites.value, (site) => {
324					return openBlock(), createElementBlock("article", {
325						key: site.url,
326						class: normalizeClass({ featured: site.featured })
327					}, [
328						site.featured ? (openBlock(), createElementBlock("span", _hoisted_29, toDisplayString(__props.labels.featured), 1)) : createCommentVNode("", true),
329						createBaseVNode("a", {
330							href: site.url,
331							target: "_blank",
332							rel: "noopener noreferrer nofollow",
333							class: "screenshot-link"
334						}, [createBaseVNode("img", {
335							src: getImagePath(site.url),
336							alt: __props.labels.screenshot_alt.replace("{name}", site.name),
337							loading: "lazy"
338						}, null, 8, _hoisted_31)], 8, _hoisted_30),
339						createBaseVNode("header", null, [createBaseVNode("h3", null, [createBaseVNode("a", {
340							href: site.url,
341							target: "_blank",
342							rel: "noopener noreferrer nofollow"
343						}, toDisplayString(site.name), 9, _hoisted_32)])]),
344						createBaseVNode("p", null, toDisplayString(site.description), 1),
345						createBaseVNode("footer", null, [
346							createBaseVNode("div", null, [createBaseVNode("strong", null, toDisplayString(__props.labels.filters.category.label_plural) + ":", 1), createTextVNode(" " + toDisplayString(getSortedLabels(site.categories, __props.labels.category).join(", ")), 1)]),
347							createBaseVNode("div", null, [createBaseVNode("strong", null, toDisplayString(__props.labels.filters.country.label) + ":", 1), createTextVNode(" " + toDisplayString(site.country ? getCountryName(site.country) : __props.labels.filters.country.global), 1)]),
348							site.features.length ? (openBlock(), createElementBlock("div", _hoisted_33, [createBaseVNode("strong", null, toDisplayString(__props.labels.filters.feature.label_plural) + ":", 1), createTextVNode(" " + toDisplayString(getSortedLabels(site.features, __props.labels.feature).join(", ")), 1)])) : createCommentVNode("", true),
349							createBaseVNode("div", null, [createBaseVNode("strong", null, toDisplayString(__props.labels.filters.framework.label) + ":", 1), createTextVNode(" " + toDisplayString(site.framework ? __props.labels.framework[site.framework] : __props.labels.framework.none), 1)]),
350							site.migrated_from.length ? (openBlock(), createElementBlock("div", _hoisted_34, [createBaseVNode("strong", null, toDisplayString(__props.labels.filters.cms.label) + ":", 1), createTextVNode(" " + toDisplayString(site.migrated_from.map((c) => __props.labels.cms[c] || c).join(", ")), 1)])) : createCommentVNode("", true),
351							(openBlock(), createElementBlock(Fragment, null, renderList([
352								"repository",
353								"user_guide",
354								"blog_post",
355								"forum_post",
356								"social_post",
357								"case_study"
358							], (key) => {
359								return openBlock(), createElementBlock(Fragment, { key }, [site[key] ? (openBlock(), createElementBlock("div", _hoisted_35, [createBaseVNode("a", {
360									href: site[key],
361									target: "_blank",
362									rel: "noopener noreferrer nofollow"
363								}, toDisplayString(__props.labels[key]), 9, _hoisted_36)])) : createCommentVNode("", true)], 64);
364							}), 64))
365						])
366					], 2);
367				}), 128))]),
368				hasMore.value ? (openBlock(), createElementBlock("button", {
369					key: 0,
370					ref: "load-more",
371					type: "button",
372					class: "load-more",
373					onClick: loadMore
374				}, toDisplayString(__props.labels.load_more), 513)) : createCommentVNode("", true)
375			]);
376		};
377	}
378}), [["__scopeId", "data-v-d06e895f"]]);
379//#endregion
380//#region src/en/showcase.md
381var _hoisted_1 = { class: "site-list" };
382var __pageData = JSON.parse("{\"title\":\"Sveltia CMS Showcase — Git-based headless CMS examples\",\"titleTemplate\":false,\"description\":\"Explore hundreds of real-world websites using Sveltia CMS across industries, built with Astro, Eleventy, Hugo, other frameworks and vanilla JavaScript.\",\"frontmatter\":{\"titleTemplate\":false,\"title\":\"Sveltia CMS Showcase — Git-based headless CMS examples\",\"description\":\"Explore hundreds of real-world websites using Sveltia CMS across industries, built with Astro, Eleventy, Hugo, other frameworks and vanilla JavaScript.\",\"head\":[[\"meta\",{\"property\":\"og:image\",\"content\":\"https://sveltiacms.app/images/highlights/showcase.webp\"}],[\"link\",{\"rel\":\"canonical\",\"href\":\"https://sveltiacms.app/en/showcase\"}]],\"aside\":false,\"labels\":{\"featured\":\"Favorite\",\"repository\":\"Repository\",\"user_guide\":\"User Guide\",\"blog_post\":\"Blog Post\",\"forum_post\":\"Forum Post\",\"social_post\":\"Social Media Post\",\"case_study\":\"Case Study\",\"screenshot_alt\":\"Screenshot of {name}\",\"no_results\":\"No matching sites were found. Try adjusting your filters. Please note that this showcase does not include political or religious sites.\",\"load_more\":\"Show More\",\"category\":{\"art\":\"Art & Culture\",\"community\":\"Community & Organization\",\"documentation\":\"Documentation & Wiki\",\"education\":\"Education & Research\",\"engineering\":\"Engineering & Manufacturing\",\"event\":\"Event & Competition\",\"fashion\":\"Fashion & Beauty\",\"health\":\"Health & Wellness\",\"hospital
382ity\":\"Hospitality & Travel\",\"media\":\"Media & Publishing\",\"personal\":\"Personal Website & Blog\",\"photography\":\"Photography & Videography\",\"portfolio\":\"Portfolio & Creative\",\"public\":\"Government & Public Sector\",\"reference\":\"Reference & Tool\",\"retail\":\"Retail & E-commerce\",\"science\":\"Science & Technology\",\"service\":\"Professional Service\",\"software\":\"Software & SaaS\",\"sport\":\"Sport & Recreation\"},\"framework\":{\"angular\":\"Angular\",\"antora\":\"Antora\",\"astro\":\"Astro\",\"blume\":\"Blume\",\"docusaurus\":\"Docusaurus\",\"eleventy\":\"Eleventy\",\"gatsby\":\"Gatsby\",\"hugo\":\"Hugo\",\"jekyll\":\"Jekyll\",\"mkdocs\":\"MkDocs\",\"react\":\"React\",\"react-router\":\"React Router\",\"middleman\":\"Middleman\",\"next\":\"Next.js\",\"nuxt\":\"Nuxt\",\"pelican\":\"Pelican\",\"solid-start\":\"SolidStart\",\"sphinx\":\"Sphinx\",\"sveltekit\":\"SvelteKit\",\"tanstack\":\"TanStack Start\",\"vitepress\":\"VitePress\",\"vue\":\"Vue\",\"zola\":\"Zola\",\"vanilla\":\"Vanilla JavaScript\",\"custom\":\"Custom Tooling\",\"none\":\"None / Headless\"},\"cms\":{\"ameba-ownd\":\"Ameba Ownd\",\"artstation\":\"ArtStation\",\"basekit\":\"BaseKit\",\"blogger\":\"Blogger\",\"bricksite\":\"Bricksite\",\"canva\":\"Canva\",\"carrd\":\"Carrd\",\"cloudcannon\":\"CloudCannon\",\"cloudrexx\":\"Cloudrexx\",\"datocms\":\"DatoCMS\",\"decap-cm
382s\":\"Decap CMS\",\"drupal\":\"Drupal\",\"duda\":\"Duda\",\"e-monsite\":\"E-monsite\",\"everweb\":\"EverWeb\",\"figma-sites\":\"Figma Sites\",\"framer\":\"Framer\",\"ghost\":\"Ghost\",\"godaddy\":\"GoDaddy Website Builder\",\"google-sites\":\"Google Sites\",\"hostinger\":\"Hostinger Website Builder\",\"infomaniak\":\"Infomaniak Site Creator\",\"jdomni\":\"JD Omni\",\"joomla\":\"Joomla\",\"journo-portfolio\":\"Journo Portfolio\",\"keystatic\":\"Keystatic\",\"mobirise\":\"Mobirise\",\"netlify-cms\":\"Netlify CMS\",\"nicepage\":\"Nicepage\",\"odoo\":\"Odoo\",\"online-gallery\":\"Online Gallery\",\"pages-cms\":\"Pages CMS\",\"podpage\":\"Podpage\",\"readymag\":\"Readymag\",\"shoppagina\":\"Shoppagina\",\"showit\":\"Showit\",\"simdif\":\"SimDif\",\"sitepins\":\"Sitepins\",\"squarespace\":\"Squarespace\",\"static-cms\":\"Static CMS\",\"strato\":\"Strato Sitebuilder\",\"taplink\":\"Taplink\",\"tilda\":\"Tilda\",\"tinacms\":\"TinaCMS\",\"vocus\":\"Vocus\",\"webflow\":\"Webflow\",\"weblium\":\"Weblium\",\"webnode\":\"Webnode\",\"website-world\":\"Website World CMS\",\"weebly\":\"Weebly\",\"wix\":\"Wix\",\"wordpress\":\"WordPress\"},\"feature\":{\"commit-messages\":\"Custom Commit Messages\",\"i18n\":\"Internationalization (i18n)\",\"image-optimization\":\"Image Optimization\",\"external-media-storage\":\"External Media Storage\",\"editorial-workflow\":\"Editorial Workflow\",\"open-authoring\":\"Open Authoring\",\"initialization\":\"Manual Initialization\",\"preview-styles\":\"Custom Preview Styles\",\"preview-templates\":\"Custom Preview Templates\",\"editor-components\":\"Custom Editor Components\",\"field-types\":\"Custom Field Types\",\"file-formats\":\"Custom File Formats\",\"events\":\"Event Hooks\"},\"filters\":{\"search\":{\"label\":\"Search\",\"placeholder\":\"Search by name, description\"},\"category\":{\"label\":\"Category\",\"label_plural\":\"Categories\",\"all\":\"All Categories\"},\"country\":{\"label\":\"Country\",\"all\":\"All Countries\",\"global\":\"Global / Unknown\"},\"feature\":{\"label\":\"Feature\",\"label_plural\":\"Features\",\"all\":\"All Features\"},\"framework\":{\"label\":\"Framework\",\"all\":\"All Frameworks\"},\"cms\":{\"label\":\"Migrated from\",\"all\":\"All Platforms\"}}}},\"headers\":[],\"relativePath\":\"en/showcase.md\",\"filePath\":\"en/showcase.md\"}");
383var _sfc_main = /*@__PURE__*/ Object.assign({ name: "en/showcase.md" }, { setup(__props) {
384	const siteCount = sites.length;
385	const countryCount = sites.map((site) => site.country).filter((value, index, self) => !!value && self.indexOf(value) === index).length;
386	return (_ctx, _cache) => {
387		return openBlock(), createElementBlock("div", null, [createBaseVNode("div", _hoisted_1, [
388			_cache[0] || (_cache[0] = createBaseVNode("h1", {
389				id: "showcase",
390				tabindex: "-1"
391			}, [createTextVNode("Showcase "), createBaseVNode("a", {
392				class: "header-anchor",
393				href: "#showcase",
394				"aria-label": "Permalink to “Showcase”"
395			}, "​")], -1)),
396			createBaseVNode("p", null, "Sveltia CMS is trusted by thousands of users around the world. Here’s a diverse collection of " + toDisplayString(unref(siteCount)) + "\xA0handpicked examples from " + toDisplayString(unref(countryCount)) + "\xA0countries, with new sites added daily. Find inspiration for your next project, and see how this modern Git-based headless CMS can be used in various scenarios.", 1),
397			createVNode(ShowcaseList_default, {
398				sites: unref(sites),
399				labels: _ctx.$frontmatter.labels
400			}, null, 8, ["sites", "labels"])
401		])]);
402	};
403} });
404//#endregion
405export { __pageData, _sfc_main as default };

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.