Add quality gates: Prettier for Astro, astro check for shared, unused SCSS variable lint (#2749)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
Paweł Kunaandcopilot-swe-agent[bot] authored and GitHub committed 2026-08-03 00:47:55 +02:00
1 parent 4d04c102d3
commit 8962710163
552 files changed
+18014 -19559

No files matched your search

+39 -50
View File
@@ -11,69 +11,58 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
const docs: string[] = sync(join(__dirname, '..', 'docs', 'pages', '**', '*.mdx')) const docs: string[] = sync(join(__dirname, '..', 'docs', 'pages', '**', '*.mdx'))
async function formatHTML(htmlString: string): Promise<string> { async function formatHTML(htmlString: string): Promise<string> {
try { try {
const formattedHtml = await prettier.format(htmlString, { const formattedHtml = await prettier.format(htmlString, {
parser: 'html', parser: 'html',
printWidth: 100, printWidth: 100,
}) })
return formattedHtml return formattedHtml
} catch (error) { } catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error) const errorMessage = error instanceof Error ? error.message : String(error)
console.error('Error formatting HTML:', errorMessage) console.error('Error formatting HTML:', errorMessage)
return htmlString // Return original in case of an error return htmlString // Return original in case of an error
} }
} }
async function replaceAsync( async function replaceAsync(str: string, regex: RegExp, asyncFn: (...args: string[]) => Promise<string>): Promise<string> {
str: string, const matches = [...str.matchAll(regex)]
regex: RegExp,
asyncFn: (...args: string[]) => Promise<string>
): Promise<string> {
const matches = [...str.matchAll(regex)]
const replacements = await Promise.all( const replacements = await Promise.all(matches.map(async (match: RegExpMatchArray) => asyncFn(...match)))
matches.map(async (match: RegExpMatchArray) => asyncFn(...match))
)
let result = str let result = str
matches.forEach((match: RegExpMatchArray, i: number) => { matches.forEach((match: RegExpMatchArray, i: number) => {
result = result.replace(match[0], replacements[i]) result = result.replace(match[0], replacements[i])
}) })
return result return result
} }
async function processFiles(): Promise<void> { async function processFiles(): Promise<void> {
for (const file of docs) { for (const file of docs) {
const oldContent = readFileSync(file, 'utf8') const oldContent = readFileSync(file, 'utf8')
// get codeblocks from markdown // get codeblocks from markdown
const content = await replaceAsync( const content = await replaceAsync(oldContent, /(```([a-z0-9]+).*?\n)(.*?)(```)/gs, async (m: string, m1: string, m2: string, m3: string, m4: string) => {
oldContent, if (m2 === 'html') {
/(```([a-z0-9]+).*?\n)(.*?)(```)/gs, let formattedHtml = await formatHTML(m3)
async (m: string, m1: string, m2: string, m3: string, m4: string) => {
if (m2 === 'html') {
let formattedHtml = await formatHTML(m3)
// remove empty lines // remove empty lines
formattedHtml = formattedHtml.replace(/^\s*[\r\n]/gm, '') formattedHtml = formattedHtml.replace(/^\s*[\r\n]/gm, '')
return m1 + formattedHtml.trim() + '\n' + m4 return m1 + formattedHtml.trim() + '\n' + m4
} }
return m.trim() return m.trim()
} })
)
if (content !== oldContent) { if (content !== oldContent) {
writeFileSync(file, content, 'utf8') writeFileSync(file, content, 'utf8')
console.log(`Reformatted ${file}`) console.log(`Reformatted ${file}`)
} }
} }
} }
processFiles().catch((error) => { processFiles().catch((error) => {
const errorMessage = error instanceof Error ? error.message : String(error) const errorMessage = error instanceof Error ? error.message : String(error)
console.error('Error processing files:', errorMessage) console.error('Error processing files:', errorMessage)
process.exit(1) process.exit(1)
}) })
+51 -60
View File
@@ -3,72 +3,63 @@ import { fileURLToPath } from 'node:url'
import { defineConfig, type UserConfig } from 'vite' import { defineConfig, type UserConfig } from 'vite'
interface CreateViteConfigOptions { interface CreateViteConfigOptions {
entry: string entry: string
name?: string name?: string
fileName: string | ((format: string) => string) fileName: string | ((format: string) => string)
formats: ('es' | 'umd' | 'iife' | 'cjs')[] formats: ('es' | 'umd' | 'iife' | 'cjs')[]
outDir: string outDir: string
banner?: string banner?: string
minify?: boolean | 'esbuild' minify?: boolean | 'esbuild'
} }
/** /**
* Creates a Vite configuration for building libraries * Creates a Vite configuration for building libraries
*/ */
export function createViteConfig({ export function createViteConfig({ entry, name, fileName, formats, outDir, banner, minify = false }: CreateViteConfigOptions): UserConfig {
entry, // Vite 8 (Rolldown) always emits const bindings and no longer accepts the
name, // Rollup-only generatedCode.constBindings option
fileName, const rollupOutput: { banner?: string } = {}
formats,
outDir,
banner,
minify = false
}: CreateViteConfigOptions): UserConfig {
// Vite 8 (Rolldown) always emits const bindings and no longer accepts the
// Rollup-only generatedCode.constBindings option
const rollupOutput: { banner?: string } = {}
// Add banner if provided // Add banner if provided
if (banner) { if (banner) {
rollupOutput.banner = banner rollupOutput.banner = banner
} }
const config: UserConfig = { const config: UserConfig = {
// These are library (JS bundle) builds, not app builds — Vite's default // These are library (JS bundle) builds, not app builds — Vite's default
// behavior of copying <root>/public into outDir on every build is not wanted // behavior of copying <root>/public into outDir on every build is not wanted
// here (and in @tabler/preview, where public/ is itself generated from this // here (and in @tabler/preview, where public/ is itself generated from this
// same outDir, caused unbounded growth across repeated builds). // same outDir, caused unbounded growth across repeated builds).
publicDir: false, publicDir: false,
build: { build: {
lib: { lib: {
entry: path.resolve(entry), entry: path.resolve(entry),
name: name, name: name,
fileName: typeof fileName === 'function' ? fileName : () => fileName, fileName: typeof fileName === 'function' ? fileName : () => fileName,
formats: formats formats: formats,
}, },
outDir: path.resolve(outDir), outDir: path.resolve(outDir),
emptyOutDir: false, emptyOutDir: false,
sourcemap: true, sourcemap: true,
rollupOptions: { rollupOptions: {
output: rollupOutput output: rollupOutput,
}, },
target: 'es2015', target: 'es2015',
minify: minify minify: minify,
}, },
define: { define: {
'process.env.NODE_ENV': '"production"' 'process.env.NODE_ENV': '"production"',
}, },
esbuild: { esbuild: {
target: 'es2015', target: 'es2015',
tsconfigRaw: { tsconfigRaw: {
compilerOptions: { compilerOptions: {
module: 'ES2020', module: 'ES2020',
target: 'ES2015' target: 'ES2015',
} },
} },
} },
} }
return defineConfig(config) return defineConfig(config)
} }
+6 -20
View File
@@ -9,38 +9,24 @@ import { readFileSync } from 'node:fs'
const __dirname = path.dirname(fileURLToPath(import.meta.url)) const __dirname = path.dirname(fileURLToPath(import.meta.url))
interface PackageJson { interface PackageJson {
version: string version: string
[key: string]: unknown [key: string]: unknown
} }
const pkg: PackageJson = JSON.parse( const pkg: PackageJson = JSON.parse(readFileSync(path.join(__dirname, '../core', 'package.json'), 'utf8'))
readFileSync(path.join(__dirname, '../core', 'package.json'), 'utf8')
)
// Create zip instance and add folder // Create zip instance and add folder
const zip = new AdmZip() const zip = new AdmZip()
zip.addLocalFolder(path.join(__dirname, '../preview/dist'), 'dashboard') zip.addLocalFolder(path.join(__dirname, '../preview/dist'), 'dashboard')
zip.addLocalFile( zip.addLocalFile(path.join(__dirname, '../shared/static', 'og.png'), '.', 'preview.png')
path.join(__dirname, '../shared/static', 'og.png'),
'.',
'preview.png'
)
zip.addFile( zip.addFile('documentation.url', Buffer.from('[InternetShortcut]\nURL = https://tabler.io/docs'))
'documentation.url',
Buffer.from('[InternetShortcut]\nURL = https://tabler.io/docs')
)
// Folder to zip and output path // Folder to zip and output path
const outputZipPath = path.join( const outputZipPath = path.join(__dirname, '../packages-zip', `tabler-${pkg.version}.zip`)
__dirname,
'../packages-zip',
`tabler-${pkg.version}.zip`
)
// Write the zip file // Write the zip file
zip.writeZip(outputZipPath) zip.writeZip(outputZipPath)
console.log(`Zipped folder to ${outputZipPath}`) console.log(`Zipped folder to ${outputZipPath}`)
@@ -0,0 +1,5 @@
---
'@tabler/core': major
---
Remove unused SCSS `!default` variables (deprecated Bootstrap leftovers such as `$variable-prefix`, `$alert-*-scale`, accordion icon/button variables, `$popover-arrow-*`, numbered `$font-size-*` / `$line-height-*` scales and others). Configuring any of the removed variables via `@use ... with (...)` now raises a Sass error — they had no effect on the compiled CSS before. The color shade ladders (`$blue-100`…`$cyan-900`) and per-color maps (`$blues`…`$cyans`) are kept as configurable API. Compiled CSS output is unchanged.
+26 -1
View File
@@ -2,4 +2,29 @@ dist
.tmp .tmp
.vscode .vscode
.cache .cache
node_modules node_modules
# generated / build outputs
.astro
coverage
demo
packages-zip
preview/public
preview/tmp-assets
docs/public
# prettier-plugin-astro cannot parse HTML comments or <script> tags inside
# JSX expressions ({cond && (<Fragment><!-- ... --><script>...)}) — these files
# use those constructs on purpose (BEGIN/END markers emitted into demo HTML,
# conditional inline scripts). Re-check when the plugin gains support.
docs/layouts/DocsLayout.astro
shared/components/cards/Card.astro
shared/components/layout/PageHeader.astro
shared/components/navbar/Navbar.astro
shared/ui/Chart.astro
shared/ui/ChartSparkline.astro
shared/ui/Datepicker.astro
shared/ui/InlinePlayer.astro
shared/ui/Map.astro
shared/ui/MapVector.astro
shared/ui/Range.astro
shared/ui/Select.astro
+7
View File
@@ -9,12 +9,19 @@
"tabWidth": 2, "tabWidth": 2,
"useTabs": false, "useTabs": false,
"trailingComma": "all", "trailingComma": "all",
"plugins": ["prettier-plugin-astro"],
"overrides": [ "overrides": [
{ {
"files": "*.scss", "files": "*.scss",
"options": { "options": {
"parser": "scss" "parser": "scss"
} }
},
{
"files": "*.astro",
"options": {
"parser": "astro"
}
} }
] ]
} }
+26 -27
View File
@@ -11,40 +11,39 @@ const __dirname = dirname(fileURLToPath(import.meta.url))
const styles: string[] = sync(join(__dirname, '..', 'dist', 'css', '*.css')) const styles: string[] = sync(join(__dirname, '..', 'dist', 'css', '*.css'))
interface Plugins { interface Plugins {
[key: string]: string [key: string]: string
} }
const plugins: Plugins = { const plugins: Plugins = {
'tabler-flags': 'Flags', 'tabler-flags': 'Flags',
'tabler-flags.rtl': 'Flags RTL', 'tabler-flags.rtl': 'Flags RTL',
'tabler-marketing': 'Marketing', 'tabler-marketing': 'Marketing',
'tabler-marketing.rtl': 'Marketing RTL', 'tabler-marketing.rtl': 'Marketing RTL',
'tabler-payments': 'Payments', 'tabler-payments': 'Payments',
'tabler-payments.rtl': 'Payments RTL', 'tabler-payments.rtl': 'Payments RTL',
'tabler-socials': 'Socials', 'tabler-socials': 'Socials',
'tabler-socials.rtl': 'Socials RTL', 'tabler-socials.rtl': 'Socials RTL',
'tabler-vendors': 'Vendors', 'tabler-vendors': 'Vendors',
'tabler-vendors.rtl': 'Vendors RTL', 'tabler-vendors.rtl': 'Vendors RTL',
} }
styles.forEach((file: string) => { styles.forEach((file: string) => {
const content = readFileSync(file, 'utf8') const content = readFileSync(file, 'utf8')
const filename = basename(file) const filename = basename(file)
const pluginKey = Object.keys(plugins).find((plugin: string) => filename.includes(plugin)) const pluginKey = Object.keys(plugins).find((plugin: string) => filename.includes(plugin))
const plugin = pluginKey ? plugins[pluginKey] : undefined const plugin = pluginKey ? plugins[pluginKey] : undefined
const regex = /^(@charset ['"][a-zA-Z0-9-]+['"];?)\n?/i const regex = /^(@charset ['"][a-zA-Z0-9-]+['"];?)\n?/i
let newContent = '' let newContent = ''
const bannerText = banner(plugin) const bannerText = banner(plugin)
if (content.match(regex)) { if (content.match(regex)) {
newContent = content.replace(regex, (m: string, m1: string) => { newContent = content.replace(regex, (m: string, m1: string) => {
return `${m1}\n${bannerText}\n` return `${m1}\n${bannerText}\n`
}) })
} else { } else {
newContent = `${bannerText}\n${content}` newContent = `${bannerText}\n${content}`
} }
writeFileSync(file, newContent, 'utf8') writeFileSync(file, newContent, 'utf8')
}) })
+50 -51
View File
@@ -12,73 +12,72 @@ const tablerPath = path.join(__dirname, '../scss/_variables.scss')
// Function to extract variable names from SCSS file // Function to extract variable names from SCSS file
function extractVariables(filePath: string): Set<string> { function extractVariables(filePath: string): Set<string> {
const content = readFileSync(filePath, 'utf8') const content = readFileSync(filePath, 'utf8')
const variables = new Set<string>() const variables = new Set<string>()
// Regex to find SCSS variables // Regex to find SCSS variables
// Looks for patterns like: $variable-name: value // Looks for patterns like: $variable-name: value
// Includes variables in maps and lists // Includes variables in maps and lists
const variableRegex = /\$([a-zA-Z0-9_-]+)\s*[:=]/g const variableRegex = /\$([a-zA-Z0-9_-]+)\s*[:=]/g
let match: RegExpExecArray | null let match: RegExpExecArray | null
while ((match = variableRegex.exec(content)) !== null) { while ((match = variableRegex.exec(content)) !== null) {
const varName = match[1] const varName = match[1]
variables.add(varName) variables.add(varName)
} }
return variables return variables
} }
// Main function // Main function
function compareVariables(): void { function compareVariables(): void {
console.log('Analyzing Bootstrap variables...') console.log('Analyzing Bootstrap variables...')
const bootstrapVars = extractVariables(bootstrapPath) const bootstrapVars = extractVariables(bootstrapPath)
console.log(`Found ${bootstrapVars.size} variables in Bootstrap\n`) console.log(`Found ${bootstrapVars.size} variables in Bootstrap\n`)
console.log('Analyzing Tabler variables...') console.log('Analyzing Tabler variables...')
const tablerVars = extractVariables(tablerPath) const tablerVars = extractVariables(tablerPath)
console.log(`Found ${tablerVars.size} variables in Tabler\n`) console.log(`Found ${tablerVars.size} variables in Tabler\n`)
// Find variables that are in Bootstrap but not in Tabler // Find variables that are in Bootstrap but not in Tabler
const missingInTabler: string[] = [] const missingInTabler: string[] = []
for (const varName of bootstrapVars) { for (const varName of bootstrapVars) {
if (!tablerVars.has(varName)) { if (!tablerVars.has(varName)) {
missingInTabler.push(varName) missingInTabler.push(varName)
} }
} }
// Sort alphabetically // Sort alphabetically
missingInTabler.sort() missingInTabler.sort()
console.log('='.repeat(60)) console.log('='.repeat(60))
console.log(`Variables in Bootstrap that are missing in Tabler: ${missingInTabler.length}`) console.log(`Variables in Bootstrap that are missing in Tabler: ${missingInTabler.length}`)
console.log('='.repeat(60)) console.log('='.repeat(60))
if (missingInTabler.length === 0) { if (missingInTabler.length === 0) {
console.log('All Bootstrap variables are present in Tabler!') console.log('All Bootstrap variables are present in Tabler!')
} else { } else {
console.log('\nList of missing variables:\n') console.log('\nList of missing variables:\n')
missingInTabler.forEach((varName: string, index: number) => { missingInTabler.forEach((varName: string, index: number) => {
console.log(`${(index + 1).toString().padStart(4)}. $${varName}`) console.log(`${(index + 1).toString().padStart(4)}. $${varName}`)
}) })
} }
// Optionally: show statistics // Optionally: show statistics
console.log('\n' + '='.repeat(60)) console.log('\n' + '='.repeat(60))
console.log('Statistics:') console.log('Statistics:')
console.log(` Bootstrap: ${bootstrapVars.size} variables`) console.log(` Bootstrap: ${bootstrapVars.size} variables`)
console.log(` Tabler: ${tablerVars.size} variables`) console.log(` Tabler: ${tablerVars.size} variables`)
console.log(` Missing: ${missingInTabler.length} variables`) console.log(` Missing: ${missingInTabler.length} variables`)
console.log(` Coverage: ${((1 - missingInTabler.length / bootstrapVars.size) * 100).toFixed(1)}%`) console.log(` Coverage: ${((1 - missingInTabler.length / bootstrapVars.size) * 100).toFixed(1)}%`)
console.log('='.repeat(60)) console.log('='.repeat(60))
} }
// Run analysis // Run analysis
try { try {
compareVariables() compareVariables()
} catch (error) { } catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error) const errorMessage = error instanceof Error ? error.message : String(error)
console.error('Error during analysis:', errorMessage) console.error('Error during analysis:', errorMessage)
process.exit(1) process.exit(1)
} }
+18 -19
View File
@@ -9,14 +9,14 @@ import { join, dirname } from 'node:path'
const __dirname = dirname(fileURLToPath(import.meta.url)) const __dirname = dirname(fileURLToPath(import.meta.url))
interface LibConfig { interface LibConfig {
npm?: string npm?: string
js?: string[] js?: string[]
css?: string[] css?: string[]
head?: boolean head?: boolean
} }
interface Libs { interface Libs {
[key: string]: LibConfig [key: string]: LibConfig
} }
const libsData = libs as Libs const libsData = libs as Libs
@@ -24,22 +24,21 @@ const libsData = libs as Libs
emptyDirSync(join(__dirname, '..', 'dist/libs')) emptyDirSync(join(__dirname, '..', 'dist/libs'))
for (const name in libsData) { for (const name in libsData) {
const { npm } = libsData[name] const { npm } = libsData[name]
if (npm) { if (npm) {
const from = join(__dirname, '..', `node_modules/${npm}`) const from = join(__dirname, '..', `node_modules/${npm}`)
const to = join(__dirname, '..', `dist/libs/${npm}`) const to = join(__dirname, '..', `dist/libs/${npm}`)
// create dir in dist/libs // create dir in dist/libs
if (!existsSync(to)) { if (!existsSync(to)) {
mkdirSync(to, { recursive: true }) mkdirSync(to, { recursive: true })
} }
copySync(from, to, { copySync(from, to, {
dereference: true, dereference: true,
}) })
console.log(`Successfully copied ${npm}`) console.log(`Successfully copied ${npm}`)
} }
} }
+94 -95
View File
@@ -8,114 +8,113 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url))
const configFile = path.join(__dirname, '../../shared/data/sri.json') const configFile = path.join(__dirname, '../../shared/data/sri.json')
interface FileConfig { interface FileConfig {
file: string file: string
configPropertyName: string configPropertyName: string
} }
const files: FileConfig[] = [ const files: FileConfig[] = [
{ {
file: 'dist/css/tabler.min.css', file: 'dist/css/tabler.min.css',
configPropertyName: 'css' configPropertyName: 'css',
}, },
{ {
file: 'dist/css/tabler.rtl.min.css', file: 'dist/css/tabler.rtl.min.css',
configPropertyName: 'css-rtl' configPropertyName: 'css-rtl',
}, },
{ {
file: 'dist/css/tabler-flags.min.css', file: 'dist/css/tabler-flags.min.css',
configPropertyName: 'css-flags' configPropertyName: 'css-flags',
}, },
{ {
file: 'dist/css/tabler-flags.rtl.min.css', file: 'dist/css/tabler-flags.rtl.min.css',
configPropertyName: 'css-flags-rtl' configPropertyName: 'css-flags-rtl',
}, },
{ {
file: 'dist/css/tabler-marketing.min.css', file: 'dist/css/tabler-marketing.min.css',
configPropertyName: 'css-marketing' configPropertyName: 'css-marketing',
}, },
{ {
file: 'dist/css/tabler-marketing.rtl.min.css', file: 'dist/css/tabler-marketing.rtl.min.css',
configPropertyName: 'css-marketing-rtl' configPropertyName: 'css-marketing-rtl',
}, },
{ {
file: 'dist/css/tabler-payments.min.css', file: 'dist/css/tabler-payments.min.css',
configPropertyName: 'css-payments' configPropertyName: 'css-payments',
}, },
{ {
file: 'dist/css/tabler-payments.rtl.min.css', file: 'dist/css/tabler-payments.rtl.min.css',
configPropertyName: 'css-payments-rtl' configPropertyName: 'css-payments-rtl',
}, },
{ {
file: 'dist/css/tabler-props.min.css', file: 'dist/css/tabler-props.min.css',
configPropertyName: 'css-props' configPropertyName: 'css-props',
}, },
{ {
file: 'dist/css/tabler-props.rtl.min.css', file: 'dist/css/tabler-props.rtl.min.css',
configPropertyName: 'css-props-rtl' configPropertyName: 'css-props-rtl',
}, },
{ {
file: 'dist/css/tabler-themes.min.css', file: 'dist/css/tabler-themes.min.css',
configPropertyName: 'css-themes' configPropertyName: 'css-themes',
}, },
{ {
file: 'dist/css/tabler-themes.rtl.min.css', file: 'dist/css/tabler-themes.rtl.min.css',
configPropertyName: 'css-themes-rtl' configPropertyName: 'css-themes-rtl',
}, },
{ {
file: 'dist/css/tabler-socials.min.css', file: 'dist/css/tabler-socials.min.css',
configPropertyName: 'css-socials' configPropertyName: 'css-socials',
}, },
{ {
file: 'dist/css/tabler-socials.rtl.min.css', file: 'dist/css/tabler-socials.rtl.min.css',
configPropertyName: 'css-socials-rtl' configPropertyName: 'css-socials-rtl',
}, },
{ {
file: 'dist/css/tabler-vendors.min.css', file: 'dist/css/tabler-vendors.min.css',
configPropertyName: 'css-vendors' configPropertyName: 'css-vendors',
}, },
{ {
file: 'dist/css/tabler-vendors.rtl.min.css', file: 'dist/css/tabler-vendors.rtl.min.css',
configPropertyName: 'css-vendors-rtl' configPropertyName: 'css-vendors-rtl',
}, },
{ {
file: 'dist/js/tabler.min.js', file: 'dist/js/tabler.min.js',
configPropertyName: 'js' configPropertyName: 'js',
}, },
{ {
file: 'dist/js/tabler-theme.min.js', file: 'dist/js/tabler-theme.min.js',
configPropertyName: 'js-theme' configPropertyName: 'js-theme',
}, },
] ]
function generateSRI(): void { function generateSRI(): void {
const sriData: Record<string, string> = {} const sriData: Record<string, string> = {}
for (const { file, configPropertyName } of files) { for (const { file, configPropertyName } of files) {
try { try {
const filePath = path.join(__dirname, '..', file) const filePath = path.join(__dirname, '..', file)
const data = readFileSync(filePath, 'utf8') const data = readFileSync(filePath, 'utf8')
const algorithm = 'sha384' const algorithm = 'sha384'
const hash = crypto.createHash(algorithm).update(data, 'utf8').digest('base64') const hash = crypto.createHash(algorithm).update(data, 'utf8').digest('base64')
const integrity = `${algorithm}-${hash}` const integrity = `${algorithm}-${hash}`
console.log(`${configPropertyName}: ${integrity}`) console.log(`${configPropertyName}: ${integrity}`)
sriData[configPropertyName] = integrity sriData[configPropertyName] = integrity
} catch (error) { } catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error) const errorMessage = error instanceof Error ? error.message : String(error)
console.error(`Error processing ${file}:`, errorMessage) console.error(`Error processing ${file}:`, errorMessage)
throw error throw error
} }
} }
writeFileSync(configFile, JSON.stringify(sriData, null, 2) + '\n', 'utf8') writeFileSync(configFile, JSON.stringify(sriData, null, 2) + '\n', 'utf8')
} }
try { try {
generateSRI() generateSRI()
} catch (error) { } catch (error) {
console.error('Failed to generate SRI:', error) console.error('Failed to generate SRI:', error)
process.exit(1) process.exit(1)
} }
+17 -18
View File
@@ -12,7 +12,7 @@ const toDir = join(__dirname, '..', 'fonts')
// Create fonts directory if it doesn't exist // Create fonts directory if it doesn't exist
if (!existsSync(toDir)) { if (!existsSync(toDir)) {
mkdirSync(toDir, { recursive: true }) mkdirSync(toDir, { recursive: true })
} }
// Copy geist-mono fonts // Copy geist-mono fonts
@@ -20,17 +20,17 @@ const monoFrom = join(fromDir, 'geist-mono')
const monoTo = join(toDir, 'geist-mono') const monoTo = join(toDir, 'geist-mono')
if (existsSync(monoFrom)) { if (existsSync(monoFrom)) {
if (!existsSync(monoTo)) { if (!existsSync(monoTo)) {
mkdirSync(monoTo, { recursive: true }) mkdirSync(monoTo, { recursive: true })
} }
copySync(monoFrom, monoTo, { copySync(monoFrom, monoTo, {
dereference: true, dereference: true,
}) })
console.log(`Successfully copied geist-mono fonts`) console.log(`Successfully copied geist-mono fonts`)
} else { } else {
console.warn(`Warning: geist-mono fonts not found at ${monoFrom}`) console.warn(`Warning: geist-mono fonts not found at ${monoFrom}`)
} }
// Copy geist-sans fonts // Copy geist-sans fonts
@@ -38,16 +38,15 @@ const sansFrom = join(fromDir, 'geist-sans')
const sansTo = join(toDir, 'geist-sans') const sansTo = join(toDir, 'geist-sans')
if (existsSync(sansFrom)) { if (existsSync(sansFrom)) {
if (!existsSync(sansTo)) { if (!existsSync(sansTo)) {
mkdirSync(sansTo, { recursive: true }) mkdirSync(sansTo, { recursive: true })
} }
copySync(sansFrom, sansTo, { copySync(sansFrom, sansTo, {
dereference: true, dereference: true,
}) })
console.log(`Successfully copied geist-sans fonts`) console.log(`Successfully copied geist-sans fonts`)
} else { } else {
console.warn(`Warning: geist-sans fonts not found at ${sansFrom}`) console.warn(`Warning: geist-sans fonts not found at ${sansFrom}`)
} }
+15 -15
View File
@@ -1,15 +1,15 @@
export default context => { export default (context) => {
return { return {
map: { map: {
inline: false, inline: false,
annotation: true, annotation: true,
sourcesContent: true sourcesContent: true,
}, },
plugins: { plugins: {
autoprefixer: { autoprefixer: {
cascade: false cascade: false,
}, },
rtlcss: context.env === 'RTL' rtlcss: context.env === 'RTL',
} },
} }
} }
+10 -11
View File
@@ -16,15 +16,14 @@ const entryPath = path.resolve(__dirname, `../js/${entryFile}`)
const entry = `${entryPath}.ts` const entry = `${entryPath}.ts`
export default createViteConfig({ export default createViteConfig({
entry: entry, entry: entry,
name: libraryName, name: libraryName,
fileName: (format) => { fileName: (format) => {
const esmSuffix = format === 'es' ? '.esm' : '' const esmSuffix = format === 'es' ? '.esm' : ''
return `${baseName}${esmSuffix}.js` return `${baseName}${esmSuffix}.js`
}, },
formats: ['es', 'umd'], formats: ['es', 'umd'],
outDir: path.resolve(__dirname, '../dist/js'), outDir: path.resolve(__dirname, '../dist/js'),
banner: bannerText, banner: bannerText,
minify: false minify: false,
}) })
-6
View File
@@ -88,12 +88,6 @@ $form-invalid-border-color-dark: tint-color($red, 40%) !default;
// Accordion dark // Accordion dark
// //
$accordion-icon-color-dark: $primary-text-emphasis-dark !default;
$accordion-icon-active-color-dark: $primary-text-emphasis-dark !default;
$accordion-button-icon-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$accordion-icon-color-dark}'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/></svg>") !default;
$accordion-button-active-icon-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$accordion-icon-active-color-dark}'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/></svg>") !default;
// //
// Carousel dark // Carousel dark
// //
+5 -92
View File
@@ -7,13 +7,11 @@
// Prefixes // Prefixes
// $prefix moved to `_settings.scss` // $prefix moved to `_settings.scss`
$variable-prefix: bs- !default; // Deprecated in v5.2.0 for the shorter `$prefix`
// Assets Base // Assets Base
$assets-base: '..' !default; $assets-base: '..' !default;
// Base Config // Base Config
$enable-social-colors: true !default;
$enable-extra-colors: true !default; $enable-extra-colors: true !default;
$enable-gradients: false !default; $enable-gradients: false !default;
$enable-shadows: true !default; $enable-shadows: true !default;
@@ -22,7 +20,6 @@ $enable-dark-mode: true !default;
$enable-negative-margins: true !default; $enable-negative-margins: true !default;
// $enable-rfs is declared in `_mixins.scss` (forwarded from rfs with `false !default`) // $enable-rfs is declared in `_mixins.scss` (forwarded from rfs with `false !default`)
$enable-cssgrid: true !default; $enable-cssgrid: true !default;
$enable-caret: true !default;
$enable-rounded: true !default; $enable-rounded: true !default;
$enable-transitions: true !default; $enable-transitions: true !default;
$enable-reduced-motion: true !default; $enable-reduced-motion: true !default;
@@ -104,6 +101,10 @@ $colors: (
// $min-contrast-ratio, $color-contrast-dark and $color-contrast-light moved to `_settings.scss` // $min-contrast-ratio, $color-contrast-dark and $color-contrast-light moved to `_settings.scss`
// Color shade ladders and per-color maps (Bootstrap parity). Nothing in the
// build consumes them yet, but they are kept as configurable API — hence
// excluded from the unused-variable lint.
// fusv-disable
$blue-100: tint-color($blue, 80%) !default; $blue-100: tint-color($blue, 80%) !default;
$blue-200: tint-color($blue, 60%) !default; $blue-200: tint-color($blue, 60%) !default;
$blue-300: tint-color($blue, 40%) !default; $blue-300: tint-color($blue, 40%) !default;
@@ -323,9 +324,9 @@ $cyans: (
'cyan-800': $cyan-800, 'cyan-800': $cyan-800,
'cyan-900': $cyan-900, 'cyan-900': $cyan-900,
) !default; ) !default;
// fusv-enable
// Spacers // Spacers
$spacer-0: 0 !default;
$spacer-1: 0.25rem !default; $spacer-1: 0.25rem !default;
$spacer-2: 0.5rem !default; $spacer-2: 0.5rem !default;
$spacer-3: 1rem !default; $spacer-3: 1rem !default;
@@ -380,7 +381,6 @@ $body-emphasis-color: $black !default;
// Typography // Typography
$font-google: null !default; $font-google: null !default;
$font-google-monospaced: null !default; $font-google-monospaced: null !default;
$font-local: null !default;
$font-icons: () !default; $font-icons: () !default;
$font-family-sans-serif: ('Geist', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif) !default; $font-family-sans-serif: ('Geist', -apple-system, BlinkMacSystemFont, San Francisco, Segoe UI, Roboto, Helvetica Neue, sans-serif) !default;
@@ -400,23 +400,6 @@ $icon-sm-size: 1rem !default;
$icon-md-size: 2.5rem !default; $icon-md-size: 2.5rem !default;
$icon-lg-size: 3.5rem !default; $icon-lg-size: 3.5rem !default;
$font-size-75: 0.75rem !default;
$font-size-100: 0.875rem !default;
$font-size-200: 1rem !default;
$font-size-300: 1.25rem !default;
$font-size-400: 1.5rem !default;
$font-size-500: 1.75rem !default;
$font-size-600: 2rem !default;
$font-size-700: 2.5rem !default;
$line-height-100: 1rem !default;
$line-height-200: 1.25rem !default;
$line-height-300: 1.5rem !default;
$line-height-400: 1.75rem !default;
$line-height-500: 2rem !default;
$line-height-600: 2.5rem !default;
$line-height-700: 3rem !default;
$font-size-root: null !default; $font-size-root: null !default;
$font-size-base: 0.875rem !default; $font-size-base: 0.875rem !default;
$font-size-sm: $font-size-base * 0.875 !default; $font-size-sm: $font-size-base * 0.875 !default;
@@ -561,13 +544,6 @@ $zindex-levels: (
) !default; ) !default;
$text-secondary-opacity: 0.7 !default; $text-secondary-opacity: 0.7 !default;
$text-secondary-light-opacity: 0.4 !default;
$text-secondary-dark-opacity: 0.8 !default;
$bg-surface: var(--#{$prefix}white) !default;
$bg-surface-secondary: var(--#{$prefix}gray-100) !default;
$bg-surface-tertiary: var(--#{$prefix}gray-50) !default;
$bg-surface-dark: var(--#{$prefix}dark) !default;
$body-text-align: null !default; $body-text-align: null !default;
$body-bg: $gray-50 !default; $body-bg: $gray-50 !default;
@@ -578,7 +554,6 @@ $body-emphasis-color: $gray-700 !default;
// Note: they were already no-ops here — the `!default` definitions earlier // Note: they were already no-ops here — the `!default` definitions earlier
// in this file always won, so their effective values were $black / $white. // in this file always won, so their effective values were $black / $white.
$text-muted: $gray-500 !default;
$text-secondary: $gray-500 !default; $text-secondary: $gray-500 !default;
$text-secondary-light: $gray-400 !default; $text-secondary-light: $gray-400 !default;
$text-secondary-dark: $gray-600 !default; $text-secondary-dark: $gray-600 !default;
@@ -622,15 +597,8 @@ $link-color: $primary !default;
$link-shade-percentage: 20% !default; $link-shade-percentage: 20% !default;
$link-hover-color: shift-color($link-color, $link-shade-percentage) !default; $link-hover-color: shift-color($link-color, $link-shade-percentage) !default;
$link-hover-decoration: null !default; $link-hover-decoration: null !default;
$stretched-link-pseudo-element: after !default;
$stretched-link-z-index: 1 !default;
// Icon links // Icon links
$icon-link-gap: 0.375rem !default;
$icon-link-underline-offset: 0.25em !default;
$icon-link-icon-size: 1em !default;
$icon-link-icon-transition: 0.2s ease-in-out transform !default;
$icon-link-icon-transform: translate3d(0.25em, 0, 0) !default;
// Paragraphs // Paragraphs
$paragraph-margin-bottom: 1rem !default; $paragraph-margin-bottom: 1rem !default;
@@ -787,7 +755,6 @@ $border-radius: 6px !default;
$border-radius-lg: 8px !default; $border-radius-lg: 8px !default;
$border-radius-xl: 1rem !default; $border-radius-xl: 1rem !default;
$border-radius-xxl: 2rem !default; $border-radius-xxl: 2rem !default;
$border-radius-2xl: $border-radius-xxl !default; // Deprecated in v5.3.0
$border-radius-pill: 100rem !default; $border-radius-pill: 100rem !default;
$border-radiuses: ( $border-radiuses: (
@@ -812,7 +779,6 @@ $icon-color: var(--#{$prefix}gray-400) !default;
$code-color: light-dark(var(--#{$prefix}gray-600), var(--#{$prefix}gray-400)) !default; $code-color: light-dark(var(--#{$prefix}gray-600), var(--#{$prefix}gray-400)) !default;
$code-bg: light-dark(var(--#{$prefix}gray-100), var(--#{$prefix}gray-900)) !default; $code-bg: light-dark(var(--#{$prefix}gray-100), var(--#{$prefix}gray-900)) !default;
$code-font-size: $font-size-reative-sm !default; $code-font-size: $font-size-reative-sm !default;
$code-line-height: 1.25rem !default;
$pre-padding: 1rem !default; $pre-padding: 1rem !default;
$pre-bg: var(--#{$prefix}bg-surface-dark) !default; $pre-bg: var(--#{$prefix}bg-surface-dark) !default;
@@ -893,7 +859,6 @@ $avatar-sizes: (
) !default; ) !default;
$avatar-border-radius: var(--#{$prefix}border-radius-pill) !default; $avatar-border-radius: var(--#{$prefix}border-radius-pill) !default;
$avatar-font-size: $h4-font-size !default; $avatar-font-size: $h4-font-size !default;
$avatar-box-shadow: var(--#{$prefix}shadow-border) !default;
$avatar-list-spacing: -0.5; $avatar-list-spacing: -0.5;
$link-decoration: none !default; $link-decoration: none !default;
@@ -902,8 +867,6 @@ $link-hover-decoration: underline !default;
// Horizontal rules // Horizontal rules
$hr-margin-y: 2rem !default; $hr-margin-y: 2rem !default;
$hr-color: inherit !default; $hr-color: inherit !default;
$hr-bg-color: null !default;
$hr-height: null !default;
$hr-border-color: null !default; $hr-border-color: null !default;
$hr-border-width: var(--#{$prefix}border-width) !default; $hr-border-width: var(--#{$prefix}border-width) !default;
$hr-opacity: $border-opacity !default; $hr-opacity: $border-opacity !default;
@@ -1065,15 +1028,10 @@ $overlay-gradient: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.
$alert-padding-y: 0.75rem !default; $alert-padding-y: 0.75rem !default;
$alert-padding-x: 1rem !default; $alert-padding-x: 1rem !default;
$alert-margin-bottom: 1rem !default; $alert-margin-bottom: 1rem !default;
$alert-border-radius: var(--#{$prefix}border-radius) !default;
$alert-link-font-weight: var(--#{$prefix}font-weight-semibold) !default; $alert-link-font-weight: var(--#{$prefix}font-weight-semibold) !default;
$alert-border-width: var(--#{$prefix}border-width) !default; $alert-border-width: var(--#{$prefix}border-width) !default;
$alert-dismissible-padding-r: $alert-padding-x * 3 !default; // 3x covers width of x plus default padding on either side
$alert-border-color: var(--#{$prefix}border-color-translucent) !default; $alert-border-color: var(--#{$prefix}border-color-translucent) !default;
$alert-shadow: rgba($dark, 0.04) 0 2px 4px 0 !default; $alert-shadow: rgba($dark, 0.04) 0 2px 4px 0 !default;
$alert-bg-scale: -80% !default; // Deprecated in v5.2.0, to be removed in v6
$alert-border-scale: -70% !default; // Deprecated in v5.2.0, to be removed in v6
$alert-color-scale: 40% !default; // Deprecated in v5.2.0, to be removed in v6
$alert-heading-margin-bottom: 0.25rem !default; $alert-heading-margin-bottom: 0.25rem !default;
$alert-icon-size: 1.25rem !default; $alert-icon-size: 1.25rem !default;
$alert-padding-inline-end: 3rem !default; $alert-padding-inline-end: 3rem !default;
@@ -1089,7 +1047,6 @@ $breadcrumb-bg: null !default;
$breadcrumb-divider-color: var(--#{$prefix}gray-500) !default; $breadcrumb-divider-color: var(--#{$prefix}gray-500) !default;
$breadcrumb-active-color: inherit !default; $breadcrumb-active-color: inherit !default;
$breadcrumb-divider: string.quote('/') !default; $breadcrumb-divider: string.quote('/') !default;
$breadcrumb-divider-flipped: $breadcrumb-divider !default;
$breadcrumb-border-radius: null !default; $breadcrumb-border-radius: null !default;
$breadcrumb-link-color: var(--#{$prefix}link-color) !default; $breadcrumb-link-color: var(--#{$prefix}link-color) !default;
$breadcrumb-active-font-weight: var(--#{$prefix}font-weight-semibold) !default; $breadcrumb-active-font-weight: var(--#{$prefix}font-weight-semibold) !default;
@@ -1105,7 +1062,6 @@ $breadcrumb-variants: (
$badge-font-size: $font-size-reative-sm !default; $badge-font-size: $font-size-reative-sm !default;
$badge-font-size-sm: $font-size-reative-xs !default; $badge-font-size-sm: $font-size-reative-xs !default;
$badge-font-size-lg: $font-size-reative-md !default; $badge-font-size-lg: $font-size-reative-md !default;
$badge-line-height: $code-line-height !default;
$badge-font-weight: var(--#{$prefix}font-weight-medium) !default; $badge-font-weight: var(--#{$prefix}font-weight-medium) !default;
$badge-padding-y: 0.25em !default; $badge-padding-y: 0.25em !default;
$badge-padding-x: 0.5em !default; $badge-padding-x: 0.5em !default;
@@ -1156,7 +1112,6 @@ $btn-border-width: $input-btn-border-width !default;
$btn-font-weight: var(--#{$prefix}font-weight-medium) !default; $btn-font-weight: var(--#{$prefix}font-weight-medium) !default;
$btn-box-shadow: var(--#{$prefix}shadow-input) !default; $btn-box-shadow: var(--#{$prefix}shadow-input) !default;
$btn-focus-width: $input-btn-focus-width !default; $btn-focus-width: $input-btn-focus-width !default;
$btn-focus-box-shadow: $input-btn-focus-box-shadow !default;
$btn-disabled-opacity: 0.4 !default; $btn-disabled-opacity: 0.4 !default;
$btn-active-box-shadow: inset 0 3px 5px rgba($black, 0.125) !default; $btn-active-box-shadow: inset 0 3px 5px rgba($black, 0.125) !default;
@@ -1267,7 +1222,6 @@ $card-border-color: var(--#{$prefix}border-color-translucent) !default;
$card-border-radius: var(--#{$prefix}border-radius-lg) !default; $card-border-radius: var(--#{$prefix}border-radius-lg) !default;
$card-box-shadow: var(--#{$prefix}shadow-card) !default; $card-box-shadow: var(--#{$prefix}shadow-card) !default;
$card-inner-border-radius: subtract($card-border-radius, $card-border-width) !default; $card-inner-border-radius: subtract($card-border-radius, $card-border-width) !default;
$card-hover-box-shadow: var(--#{$prefix}shadow-card-hover) !default;
$card-cap-padding-y: $card-spacer-y !default; $card-cap-padding-y: $card-spacer-y !default;
$card-cap-padding-x: $card-spacer-x !default; $card-cap-padding-x: $card-spacer-x !default;
@@ -1276,7 +1230,6 @@ $card-cap-color: inherit !default;
$card-height: null !default; $card-height: null !default;
$card-color: inherit !default; $card-color: inherit !default;
$card-bg: var(--#{$prefix}bg-surface) !default; $card-bg: var(--#{$prefix}bg-surface) !default;
$card-bg-hover: $white !default;
$card-img-overlay-padding: $spacer !default; $card-img-overlay-padding: $spacer !default;
$card-group-margin: 1.5rem !default; $card-group-margin: 1.5rem !default;
@@ -1324,10 +1277,7 @@ $ribbon-bookmark-notch-width: 0.5rem !default;
// Accordion // Accordion
$accordion-padding-y: 1rem !default; $accordion-padding-y: 1rem !default;
$accordion-padding-x: 1.25rem !default; $accordion-padding-x: 1.25rem !default;
$accordion-color: var(--#{$prefix}body-color) !default;
$accordion-bg: transparent !default;
$accordion-border-width: var(--#{$prefix}border-width) !default; $accordion-border-width: var(--#{$prefix}border-width) !default;
$accordion-border-color: var(--#{$prefix}border-color-translucent) !default;
$accordion-border-radius: var(--#{$prefix}border-radius) !default; $accordion-border-radius: var(--#{$prefix}border-radius) !default;
$accordion-inner-border-radius: subtract($accordion-border-radius, $accordion-border-width) !default; $accordion-inner-border-radius: subtract($accordion-border-radius, $accordion-border-width) !default;
$accordion-btn-toggle-width: $icon-size !default; $accordion-btn-toggle-width: $icon-size !default;
@@ -1336,25 +1286,8 @@ $accordion-body-padding-y: $accordion-padding-y !default;
$accordion-body-padding-x: $accordion-padding-x !default; $accordion-body-padding-x: $accordion-padding-x !default;
$accordion-button-padding-y: $accordion-padding-y !default; $accordion-button-padding-y: $accordion-padding-y !default;
$accordion-button-padding-x: $accordion-padding-x !default;
$accordion-button-color: var(--#{$prefix}body-color) !default;
$accordion-button-bg: transparent !default; $accordion-button-bg: transparent !default;
$accordion-transition:
$btn-transition,
border-radius 0.15s ease !default;
$accordion-button-active-bg: transparent !default;
$accordion-button-active-color: inherit !default; $accordion-button-active-color: inherit !default;
$accordion-button-focus-border-color: $accordion-border-color !default; // Deprecated in v5.3.3
$accordion-button-focus-box-shadow: $btn-focus-box-shadow !default;
$accordion-icon-width: 1rem !default;
$accordion-icon-color: $body-color !default;
$accordion-icon-active-color: $primary-text-emphasis !default;
$accordion-icon-transition: transform 0.2s ease-in-out !default;
$accordion-icon-transform: rotate(-180deg) !default;
$accordion-button-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='#{$accordion-icon-color}' stroke-linecap='round' stroke-linejoin='round'><path d='m2 5 6 6 6-6'/></svg>") !default;
$accordion-button-active-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='#{$accordion-icon-active-color}' stroke-linecap='round' stroke-linejoin='round'><path d='m2 5 6 6 6-6'/></svg>") !default;
$accordion-btn-gap: 0.75rem !default; $accordion-btn-gap: 0.75rem !default;
$accordion-header-gap: 1rem !default; $accordion-header-gap: 1rem !default;
$accordion-tabs-gap: 0.75rem !default; $accordion-tabs-gap: 0.75rem !default;
@@ -1398,7 +1331,6 @@ $carousel-indicators-vertical-margin-inline-end: 1rem !default;
// Close // Close
$btn-close-width: 1em !default; $btn-close-width: 1em !default;
$btn-close-height: $btn-close-width !default;
$btn-close-padding-x: 0.25em !default; $btn-close-padding-x: 0.25em !default;
$btn-close-padding-y: $btn-close-padding-x !default; $btn-close-padding-y: $btn-close-padding-x !default;
$btn-close-color: $body-color !default; $btn-close-color: $body-color !default;
@@ -1456,7 +1388,6 @@ $dropdown-item-padding-x: 0.75rem !default;
$dropdown-header-color: $gray-600 !default; $dropdown-header-color: $gray-600 !default;
$dropdown-header-padding-x: $dropdown-item-padding-x !default; $dropdown-header-padding-x: $dropdown-item-padding-x !default;
$dropdown-header-padding-y: $dropdown-padding-y !default; $dropdown-header-padding-y: $dropdown-padding-y !default;
$dropdown-header-padding: $dropdown-header-padding-y $dropdown-header-padding-x !default; // Deprecated in v5.2.0
$dropdown-max-width: 25rem !default; $dropdown-max-width: 25rem !default;
$dropdown-scrollable-height: 13rem !default; $dropdown-scrollable-height: 13rem !default;
@@ -1495,7 +1426,6 @@ $tooltip-margin: null !default; // TODO: remove this in v6
$tooltip-arrow-width: 0.8rem !default; $tooltip-arrow-width: 0.8rem !default;
$tooltip-arrow-height: 0.4rem !default; $tooltip-arrow-height: 0.4rem !default;
$tooltip-arrow-color: null !default;
// Loader // Loader
$loader-size: 2.5rem !default; $loader-size: 2.5rem !default;
@@ -1565,7 +1495,6 @@ $nav-link-transition:
border-color 0.15s ease-in-out !default; border-color 0.15s ease-in-out !default;
$nav-link-disabled-color: var(--#{$prefix}disabled-color) !default; $nav-link-disabled-color: var(--#{$prefix}disabled-color) !default;
$nav-link-focus-box-shadow: $focus-ring-box-shadow !default; $nav-link-focus-box-shadow: $focus-ring-box-shadow !default;
$nav-link-height: $font-size-base * $line-height-base + $nav-link-padding-y * 2 !default;
$nav-link-icon-size: $icon-size !default; $nav-link-icon-size: $icon-size !default;
$nav-link-icon-color: color-transparent(var(--#{$prefix}nav-link-color), 0.5) !default; $nav-link-icon-color: color-transparent(var(--#{$prefix}nav-link-color), 0.5) !default;
$nav-link-hover-icon-color: color-transparent(var(--#{$prefix}nav-link-color), 0.8) !default; $nav-link-hover-icon-color: color-transparent(var(--#{$prefix}nav-link-color), 0.8) !default;
@@ -1588,7 +1517,6 @@ $nav-tabs-link-hover-border-color: $nav-tabs-border-color $nav-tabs-border-color
$nav-tabs-link-active-color: var(--#{$prefix}body-color) !default; $nav-tabs-link-active-color: var(--#{$prefix}body-color) !default;
$nav-tabs-link-active-bg: var(--#{$prefix}body-bg) !default; $nav-tabs-link-active-bg: var(--#{$prefix}body-bg) !default;
$nav-tabs-link-active-border-color: $nav-tabs-link-hover-border-color !default; $nav-tabs-link-active-border-color: $nav-tabs-link-hover-border-color !default;
$nav-tabs-bg: var(--#{$prefix}bg-surface-tertiary) !default;
$nav-underline-gap: 1rem !default; $nav-underline-gap: 1rem !default;
$nav-underline-border-width: 0.125rem !default; $nav-underline-border-width: 0.125rem !default;
@@ -1626,17 +1554,14 @@ $navbar-dark-hover-color: rgba($white, 0.75) !default;
$navbar-dark-brand-color: $white !default; $navbar-dark-brand-color: $white !default;
$navbar-dark-active-color: $white !default; $navbar-dark-active-color: $white !default;
$navbar-dark-disabled-color: var(--#{$prefix}disabled-color) !default; $navbar-dark-disabled-color: var(--#{$prefix}disabled-color) !default;
$navbar-dark-active-bg: rgba(255, 255, 255, 0.06) !default;
$navbar-dark-icon-color: $navbar-dark-color !default; $navbar-dark-icon-color: $navbar-dark-color !default;
$navbar-dark-toggler-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path stroke='#{$navbar-dark-icon-color}' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>") !default; $navbar-dark-toggler-icon-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'><path stroke='#{$navbar-dark-icon-color}' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/></svg>") !default;
$navbar-dark-toggler-border-color: rgba($white, 0.1) !default; $navbar-dark-toggler-border-color: rgba($white, 0.1) !default;
$navbar-dark-brand-hover-color: $navbar-dark-active-color !default; $navbar-dark-brand-hover-color: $navbar-dark-active-color !default;
$navbar-brand-font-size: $h2-font-size !default; $navbar-brand-font-size: $h2-font-size !default;
$navbar-brand-height: $navbar-brand-font-size * $line-height-base !default;
$navbar-brand-padding-y: $nav-link-padding-y !default; $navbar-brand-padding-y: $nav-link-padding-y !default;
$navbar-brand-image-height: 2rem !default; $navbar-brand-image-height: 2rem !default;
$navbar-brand-margin-right: 0 !default;
$navbar-brand-margin-end: 1rem !default; $navbar-brand-margin-end: 1rem !default;
$navbar-brand-font-weight: var(--#{$prefix}font-weight-semibold) !default; $navbar-brand-font-weight: var(--#{$prefix}font-weight-semibold) !default;
@@ -1683,8 +1608,6 @@ $popover-body-padding-x: 0.5rem !default;
$popover-arrow-width: 1rem !default; $popover-arrow-width: 1rem !default;
$popover-arrow-height: 0.5rem !default; $popover-arrow-height: 0.5rem !default;
$popover-arrow-color: $popover-bg !default;
$popover-arrow-outer-color: var(--#{$prefix}border-color-translucent) !default;
// Footer // Footer
$footer-padding-y: 2rem !default; $footer-padding-y: 2rem !default;
@@ -1754,7 +1677,6 @@ $status-circle-size: 0.75rem !default;
$steps-border-width: 2px !default; $steps-border-width: 2px !default;
$steps-color: var(--#{$prefix}primary) !default; $steps-color: var(--#{$prefix}primary) !default;
$steps-inactive-color: var(--#{$prefix}border-color) !default; $steps-inactive-color: var(--#{$prefix}border-color) !default;
$steps-margin: 2rem 0 !default;
$steps-dot-size: 0.5rem !default; $steps-dot-size: 0.5rem !default;
$step-item-min-height: 1rem !default; $step-item-min-height: 1rem !default;
$steps-counter-dot-size: 1.5rem !default; $steps-counter-dot-size: 1.5rem !default;
@@ -1777,7 +1699,6 @@ $spinner-border-width-sm: 1px !default;
// Tables // Tables
$table-bg: transparent !default; $table-bg: transparent !default;
$table-bg-scale-dark: 40% !default;
$table-color: inherit !default; $table-color: inherit !default;
$table-accent-bg: transparent !default; $table-accent-bg: transparent !default;
@@ -1791,11 +1712,9 @@ $table-border-factor: 0.2 !default;
$table-border-width: var(--#{$prefix}border-width) !default; $table-border-width: var(--#{$prefix}border-width) !default;
$table-border-color: var(--#{$prefix}border-color-translucent) !default; $table-border-color: var(--#{$prefix}border-color-translucent) !default;
$table-th-border-color: var(--#{$prefix}border-color-translucent) !default;
$table-th-padding-x: $table-cell-padding-x !default; $table-th-padding-x: $table-cell-padding-x !default;
$table-th-padding-y: 0.5rem !default; $table-th-padding-y: 0.5rem !default;
$table-th-font-weight: null !default; $table-th-font-weight: null !default;
$table-th-color: var(--#{$prefix}gray-500) !default;
$table-th-bg: var(--#{$prefix}bg-surface-tertiary) !default; $table-th-bg: var(--#{$prefix}bg-surface-tertiary) !default;
$table-striped-color: $table-color !default; $table-striped-color: $table-color !default;
@@ -1887,8 +1806,6 @@ $list-group-border-radius: var(--#{$prefix}border-radius) !default;
$list-group-item-padding-y: $card-cap-padding-y !default; $list-group-item-padding-y: $card-cap-padding-y !default;
$list-group-item-padding-x: $card-cap-padding-x !default; $list-group-item-padding-x: $card-cap-padding-x !default;
$list-group-item-bg-scale: -80% !default; // Deprecated in v5.3.0
$list-group-item-color-scale: 40% !default; // Deprecated in v5.3.0
$list-group-hover-bg: $hover-bg !default; $list-group-hover-bg: $hover-bg !default;
$list-group-active-color: inherit !default; $list-group-active-color: inherit !default;
@@ -1941,7 +1858,6 @@ $form-check-input-box-shadow: $input-box-shadow !default;
$form-check-input-checked-bg-size: 1.25rem !default; $form-check-input-checked-bg-size: 1.25rem !default;
$form-check-input-checked-bg-color: var(--#{$prefix}primary) !default; $form-check-input-checked-bg-color: var(--#{$prefix}primary) !default;
$form-check-input-checked-color: var(--#{$prefix}bg-forms) !default;
$form-check-input-checked-bg-repeat: repeat !default; $form-check-input-checked-bg-repeat: repeat !default;
$form-check-input-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path fill='none' stroke='#{$white}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/></svg>") !default; $form-check-input-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path fill='none' stroke='#{$white}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/></svg>") !default;
$form-check-input-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path fill='none' stroke='#{$body-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/></svg>") !default; $form-check-input-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='16' height='16'><path fill='none' stroke='#{$body-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8.5l2.5 2.5l5.5 -5.5'/></svg>") !default;
@@ -2006,7 +1922,6 @@ $form-switch-focus-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.o
$form-switch-checked-color: white !default; $form-switch-checked-color: white !default;
$form-switch-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-checked-color}'/></svg>") !default; $form-switch-checked-bg-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'><circle r='3' fill='#{$form-switch-checked-color}'/></svg>") !default;
$form-switch-checked-bg-position: right center !default; $form-switch-checked-bg-position: right center !default;
$form-switch-bg-size: auto !default;
// Form range // Form range
$form-range-track-width: 100% !default; $form-range-track-width: 100% !default;
@@ -2070,8 +1985,6 @@ $form-feedback-font-style: $form-text-font-style !default;
$form-feedback-valid-color: $success !default; $form-feedback-valid-color: $success !default;
$form-feedback-invalid-color: $danger !default; $form-feedback-invalid-color: $danger !default;
$form-feedback-icon-valid-color: $form-feedback-valid-color !default;
$form-feedback-icon-invalid-color: $form-feedback-invalid-color !default;
$form-feedback-icon-valid: str-replace( $form-feedback-icon-valid: str-replace(
url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='" + $green + "' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'></polyline></svg>"), url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='" + $green + "' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='20 6 9 17 4 12'></polyline></svg>"),
'#', '#',
-2
View File
@@ -1,7 +1,5 @@
@use '../config' as *; @use '../config' as *;
$select-color-dropdown: #fff;
$select-color-dropdown-border-top: red;
$input-border-width: 1px; $input-border-width: 1px;
:root { :root {
+70 -47
View File
@@ -1,55 +1,78 @@
// Rebuild Astro's public directory from source and generated workspace assets. // Rebuild Astro's public directory from source and generated workspace assets.
import { cpSync, existsSync, mkdirSync, rmSync } from 'node:fs'; import { cpSync, existsSync, mkdirSync, rmSync } from 'node:fs'
import { dirname, join } from 'node:path'; import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'; import { fileURLToPath } from 'node:url'
import { setTimeout as sleep } from 'node:timers/promises'
const root = join(dirname(fileURLToPath(import.meta.url)), '..'); const root = join(dirname(fileURLToPath(import.meta.url)), '..')
const repo = join(root, '..'); const repo = join(root, '..')
const publicDir = join(root, 'public'); const publicDir = join(root, 'public')
const copies = [ const copies = [
{ {
from: join(root, 'assets'), from: join(root, 'assets'),
to: publicDir, to: publicDir,
packageName: '@tabler/docs', packageName: '@tabler/docs',
requiredFile: join(root, 'assets', 'css', 'docs.css'), requiredFile: join(root, 'assets', 'css', 'docs.css'),
}, },
{ {
from: join(repo, 'core', 'dist'), // core's own `dev` builds this asynchronously (nodemon-driven watch), so in
to: join(publicDir, 'dist'), // `pnpm run dev` this script can start before it exists — wait for it instead
packageName: '@tabler/core', // of failing outright. See preview/.build/copy-assets.mjs for the same race.
requiredFile: join(repo, 'core', 'dist', 'css', 'tabler.css'), from: join(repo, 'core', 'dist'),
}, to: join(publicDir, 'dist'),
{ packageName: '@tabler/core',
// Sourced from preview's isolated tmp-assets/ (not dist/) — dist/ is Astro's own requiredFile: join(repo, 'core', 'dist', 'css', 'tabler.css'),
// build output there, and reading demo assets from it caused unbounded growth waitForBuild: true,
// across repeated builds. See preview/.build/copy-assets.mjs. },
from: join(repo, 'preview', 'tmp-assets'), {
to: join(publicDir, 'preview'), // Sourced from preview's isolated tmp-assets/ (not dist/) — dist/ is Astro's own
packageName: '@tabler/preview', // build output there, and reading demo assets from it caused unbounded growth
requiredFile: join(repo, 'preview', 'tmp-assets', 'css', 'demo.css'), // across repeated builds. See preview/.build/copy-assets.mjs.
}, from: join(repo, 'preview', 'tmp-assets'),
{ to: join(publicDir, 'preview'),
from: join(repo, 'shared', 'static'), packageName: '@tabler/preview',
to: join(publicDir, 'static'), requiredFile: join(repo, 'preview', 'tmp-assets', 'css', 'demo.css'),
packageName: 'shared assets', waitForBuild: true,
requiredFile: join(repo, 'shared', 'static', 'logo.svg'), },
}, {
]; from: join(repo, 'shared', 'static'),
to: join(publicDir, 'static'),
packageName: 'shared assets',
requiredFile: join(repo, 'shared', 'static', 'logo.svg'),
},
]
rmSync(publicDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }); const BUILD_WAIT_TIMEOUT_MS = 120_000
mkdirSync(publicDir, { recursive: true }); const BUILD_WAIT_POLL_MS = 200
for (const { from, to, packageName, requiredFile } of copies) { async function waitForBuildOutput(requiredFile, packageName) {
if (!existsSync(from) || !existsSync(requiredFile)) { if (existsSync(requiredFile)) return
throw new Error(`copy-assets: missing ${requiredFile} — build ${packageName} first`); console.log(`copy-assets: waiting for ${packageName} to finish its first build (${requiredFile})...`)
} const deadline = Date.now() + BUILD_WAIT_TIMEOUT_MS
while (!existsSync(requiredFile)) {
cpSync(from, to, { if (Date.now() > deadline) {
recursive: true, throw new Error(`copy-assets: timed out waiting for ${requiredFile} — is ${packageName}'s dev/build running?`)
dereference: true, }
filter: src => !src.includes('/.vscode') && !src.includes('\\.vscode'), await sleep(BUILD_WAIT_POLL_MS)
}); }
} }
console.log('copy-assets: done'); rmSync(publicDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 })
mkdirSync(publicDir, { recursive: true })
for (const { from, to, packageName, requiredFile, waitForBuild } of copies) {
if (waitForBuild) {
await waitForBuildOutput(requiredFile, packageName)
} else if (!existsSync(from) || !existsSync(requiredFile)) {
throw new Error(`copy-assets: missing ${requiredFile} — build ${packageName} first`)
}
cpSync(from, to, {
recursive: true,
dereference: true,
filter: (src) => !src.includes('/.vscode') && !src.includes('\\.vscode'),
})
}
console.log('copy-assets: done')
+2
View File
@@ -25,6 +25,8 @@ export default defineConfig({
'@ui': fileURLToPath(new URL('../shared/ui', import.meta.url)), '@ui': fileURLToPath(new URL('../shared/ui', import.meta.url)),
// docs-only components (Example, DocsMenu, …) // docs-only components (Example, DocsMenu, …)
'@components': fileURLToPath(new URL('./components', import.meta.url)), '@components': fileURLToPath(new URL('./components', import.meta.url)),
// docs-only layouts (DocsLayout + the MDX adapter, referenced by `layout:` front matter)
'@layouts': fileURLToPath(new URL('./layouts', import.meta.url)),
// this package's pages dir — used by @shared/lib/docs-children's glob // this package's pages dir — used by @shared/lib/docs-children's glob
'@pages': fileURLToPath(new URL('./pages', import.meta.url)), '@pages': fileURLToPath(new URL('./pages', import.meta.url)),
}, },
+3 -3
View File
@@ -4,7 +4,7 @@ import { site } from '@shared/lib/site.ts'
--- ---
<Code <Code
lang="html" lang="html"
code={`<link rel="stylesheet" href="${site.cdnUrl}/dist/css/tabler.min.css" /> code={`<link rel="stylesheet" href="${site.cdnUrl}/dist/css/tabler.min.css" />
<script src="${site.cdnUrl}/dist/js/tabler.min.js"></script>`} <script src="${site.cdnUrl}/dist/js/tabler.min.js"></script>`}
/> />
+8 -6
View File
@@ -3,15 +3,17 @@ import { Code } from 'astro:components'
import { site } from '@shared/lib/site.ts' import { site } from '@shared/lib/site.ts'
interface Props { interface Props {
plugins: string[], plugins: string[]
} }
const { plugins }: Props = Astro.props const { plugins }: Props = Astro.props
--- ---
<Code <Code
lang="html" lang="html"
code={plugins.map(plugin => { code={plugins
return `<link rel="stylesheet" href="${site.cdnUrl}/dist/css/tabler-${plugin}.min.css" />` .map((plugin) => {
}).join('\n')} return `<link rel="stylesheet" href="${site.cdnUrl}/dist/css/tabler-${plugin}.min.css" />`
/> })
.join('\n')}
/>
+13 -11
View File
@@ -1,18 +1,20 @@
--- ---
interface Props { interface Props {
colors: Record<string, { prop: string; title: string }>; colors: Record<string, { prop: string; title: string }>
} }
const { colors }: Props = Astro.props; const { colors }: Props = Astro.props
--- ---
<div class="row row-cols-4 row-cols-md-6 g-3 g-md-4"> <div class="row row-cols-4 row-cols-md-6 g-3 g-md-4">
{Object.values(colors).map((color) => { {
return ( Object.values(colors).map((color) => {
<div class="col-2 text-center"> return (
<div class="p-6 rounded border" style={{ backgroundColor: `var(${color.prop})` }}></div> <div class="col-2 text-center">
<div class="small">{color.title}</div> <div class="p-6 rounded border" style={{ backgroundColor: `var(${color.prop})` }} />
</div> <div class="small">{color.title}</div>
) </div>
})} )
</div> })
}
</div>
+28 -26
View File
@@ -1,36 +1,38 @@
--- ---
import Icon from "@ui/Icon.astro"; import Icon from '@ui/Icon.astro'
interface Props { interface Props {
/** When omitted, the card renders as a non-interactive "Coming soon" tile. */ /** When omitted, the card renders as a non-interactive "Coming soon" tile. */
href?: string; href?: string
icon?: string; icon?: string
title: string; title: string
description: string; description: string
} }
const { href, icon, title, description }: Props = Astro.props; const { href, icon, title, description }: Props = Astro.props
const Tag = href ? 'a' : 'div'; const Tag = href ? 'a' : 'div'
--- ---
<div class="col-md-6"> <div class="col-md-6">
<Tag href={href} class:list={['card', !href && 'bg-surface-tertiary']}> <Tag href={href} class:list={['card', !href && 'bg-surface-tertiary']}>
<div class="card-body"> <div class="card-body">
<div class="position-relative"> <div class="position-relative">
{ !href && <span class="badge position-absolute top-0 end-0">Coming soon</span> } {!href && <span class="badge position-absolute top-0 end-0">Coming soon</span>}
<div class="row"> <div class="row">
{ icon && ( {
<div class="col-auto"> icon && (
<Icon name={icon} class="text-secondary" /> <div class="col-auto">
</div> <Icon name={icon} class="text-secondary" />
)} </div>
<div class="col"> )
<div class="h3">{ title }</div> }
<p class="text-secondary m-0 fs-4 lh-base">{ description }</p> <div class="col">
</div> <div class="h3">{title}</div>
</div> <p class="text-secondary m-0 fs-4 lh-base">{description}</p>
</div> </div>
</div> </div>
</Tag> </div>
</div>
</Tag>
</div> </div>
+9 -12
View File
@@ -3,16 +3,13 @@
--- ---
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="32" fill="none"> <svg xmlns="http://www.w3.org/2000/svg" width="120" height="32" fill="none">
<path <path
fill="currentColor" fill="currentColor"
d="M78.263 10.957a5.96 5.96 0 0 1 3.158.895c.947.632 1.737 1.474 2.21 2.474.58 1.105.843 2.368.843 3.631 0 1.369-.264 2.527-.79 3.632-.474 1-1.263 1.895-2.21 2.526-.948.58-2 .895-3.106.895a5.45 5.45 0 0 1-2.263-.474c-.684-.316-1.263-.684-1.79-1.21v.158c0 .42-.157.79-.42 1.105-.263.316-.632.421-1.105.421-.79 0-1.474-.632-1.527-1.421V7.536c0-.42.158-.79.421-1.105a1.24 1.24 0 0 1 1.106-.421c.42 0 .789.158 1.105.421.263.316.42.684.42 1.105V12.8c.475-.579 1.053-1 1.685-1.315.684-.369 1.474-.527 2.263-.527ZM51.737 21.852a.82.82 0 0 1 .631.316c.21.21.316.526.316.895 0 .473-.263.842-.737 1.158-.473.315-1.052.473-1.684.473-.947.053-1.842-.21-2.631-.684-.737-.421-1.053-1.421-1.053-2.842V14.22H45.42c-.737.052-1.368-.58-1.421-1.316v-.106c0-.368.158-.736.421-1 .263-.263.632-.368 1-.368h1.158V9.799c0-.42.158-.789.421-1.105a1.24 1.24 0 0 1 1.105-.42c.421 0 .79.157 1.053.42.263.316.42.684.42 1.105v1.632h1.79c.737-.053 1.369.579 1.421 1.316v.105c0 .737-.631 1.368-1.368 1.368h-1.84v6.843c0 .368.105.631.263.79.21.157.421.262.737.262.108 0 .217-.024.359-.057.065-.015.137-.032.22-.048.21-.105.368-.158.579-.158Zm14.737-10.895c.79 0 1.473.632 1.526 1.421v10.79c0 .42-.158.79-.421 1.105-.263.316-.632.421-1.105.421a1.47 1.47 0 0 1-1.053-.42c-.263-.317-.421-.685-.421-1.106a5.664 5.664 0 0 1-1.684 1.263c-.684.316-1.474.526-2.263.526a5.959 5.959 0 0 1-3.158-.894c-.948-.632-1.737-1.474-2.21-2.474a7.842 7.842 0 0 1-.843-3.632c0-1.368.263-2.526.79-3.631.473-1 1.263-1.895 2.21-2.527.947-.579 2-.894 3.105-.894 1.527 0 2.948.526 4.053 1.579 0-.421.158-.79.421-1.106a1.144 1.144 0 0 1 1.053-.42Zm-2.421 10a4.737 4.737 0 0 0 1.052-3c0-1.21-.368-2.21-1.052-3.052a3.316 3.316 0 0 0-2.685-1.21c-1.105 0-1.947.42-2.631 1.21-.684.842-1.053 1.947-1.053 3.052 0 1.21.369 2.21 1 3 .684.79 1.684 1.264 2.684 1.21 1.106 0 2-.42 2.685-1.21Zm16.473 0c.684-.842 1.053-1.947 1.053-3.052 0-1.21-.316-2.21-1-3a3.405 3.405 0 0 0-2.684-1.21c-1.105 0-2 .42-2.684 1.21a4.842 4.842 0 0 0-1.053 3c0 1.21.316 2.21 1.052 3.052.685.79 1.685 1.264 2.685 1.21 1.105 0 1.947-.42 2.631-1.21Zm9.79 2.21c0 .422-.158.79-.421 1.106-.263.316-.684.421-1.105.421a1.47 1.47 0 0 1-1.053-.42c-.263-.317-.421-.685-.421-1.106V7.536c0-.42.158-.79.42-1.105.317-.316.685-.474 1.106-.421.421 0 .79.158 1.053.421.316.316.42.684.42 1.105v15.632Zm14.947-4.525c.316-.264.474-.58.474-1 0-2.369-1.263-4.58-3.105-5.843-1-.579-2.053-.894-3.158-.894-1.158 0-2.21.316-3.21.894a6.563 6.563 0 0 0-2.422 2.527c-.579 1.052-.895 2.316-.895 3.79a6.729 6.729 0 0 0 .843 3.526c.578 1 1.368 1.842 2.42 2.42 1.053.58 2.264.895 3.685.895.842 0 1.631-.158 2.421-.42a6.38 6.38 0 0 0 1.895-1c.473-.316.684-.738.684-1.211 0-.369-.106-.632-.369-.895a1.153 1.153 0 0 0-.894-.368 1.67 1.67 0 0 0-.843.21l-.473.263c-.316.21-.632.316-.948.421-.421.158-.947.21-1.631.21a3.805 3.805 0 0 1-3.842-3.157h8.316c.421 0 .736-.105 1.052-.368Zm-5.947-4.948c.789 0 1.526.263 2.105.737s1 1.105 1.053 1.842v.158h-6.632c.368-1.842 1.526-2.737 3.474-2.737Zm17.21-2.316c-.315-.263-.737-.42-1.263-.42-.789 0-1.579.157-2.263.526-.737.368-1.316.894-1.684 1.579v-.316c0-.421-.105-.79-.421-1.105a1.695 1.695 0 0 0-1.106-.421 1.239 1.239 0 0 0-1.105.42c-.263.316-.421.685-.421 1.106v10.368c0 .421.158.79.421 1.106.316.263.632.42 1.105.42.474 0 .843-.105 1.106-.42.263-.316.421-.685.421-1.106v-6c-.053-.684.105-1.316.368-1.947.211-.474.579-.895 1-1.21.527-.37 1.211-.474 1.842-.316l.316.105c.316.105.579.158.842.158.369.052.684-.106.948-.369.315-.263.421-.631.421-1.158 0-.368-.211-.736-.527-1Z" d="M78.263 10.957a5.96 5.96 0 0 1 3.158.895c.947.632 1.737 1.474 2.21 2.474.58 1.105.843 2.368.843 3.631 0 1.369-.264 2.527-.79 3.632-.474 1-1.263 1.895-2.21 2.526-.948.58-2 .895-3.106.895a5.45 5.45 0 0 1-2.263-.474c-.684-.316-1.263-.684-1.79-1.21v.158c0 .42-.157.79-.42 1.105-.263.316-.632.421-1.105.421-.79 0-1.474-.632-1.527-1.421V7.536c0-.42.158-.79.421-1.105a1.24 1.24 0 0 1 1.106-.421c.42 0 .789.158 1.105.421.263.316.42.684.42 1.105V12.8c.475-.579 1.053-1 1.685-1.315.684-.369 1.474-.527 2.263-.527ZM51.737 21.852a.82.82 0 0 1 .631.316c.21.21.316.526.316.895 0 .473-.263.842-.737 1.158-.473.315-1.052.473-1.684.473-.947.053-1.842-.21-2.631-.684-.737-.421-1.053-1.421-1.053-2.842V14.22H45.42c-.737.052-1.368-.58-1.421-1.316v-.106c0-.368.158-.736.421-1 .263-.263.632-.368 1-.368h1.158V9.799c0-.42.158-.789.421-1.105a1.24 1.24 0 0 1 1.105-.42c.421 0 .79.157 1.053.42.263.316.42.684.42 1.105v1.632h1.79c.737-.053 1.369.579 1.421 1.316v.105c0 .737-.631 1.368-1.368 1.368h-1.84v6.843c0 .368.105.631.263.79.21.157.421.262.737.262.108 0 .217-.024.359-.057.065-.015.137-.032.22-.048.21-.105.368-.158.579-.158Zm14.737-10.895c.79 0 1.473.632 1.526 1.421v10.79c0 .42-.158.79-.421 1.105-.263.316-.632.421-1.105.421a1.47 1.47 0 0 1-1.053-.42c-.263-.317-.421-.685-.421-1.106a5.664 5.664 0 0 1-1.684 1.263c-.684.316-1.474.526-2.263.526a5.959 5.959 0 0 1-3.158-.894c-.948-.632-1.737-1.474-2.21-2.474a7.842 7.842 0 0 1-.843-3.632c0-1.368.263-2.526.79-3.631.473-1 1.263-1.895 2.21-2.527.947-.579 2-.894 3.105-.894 1.527 0 2.948.526 4.053 1.579 0-.421.158-.79.421-1.106a1.144 1.144 0 0 1 1.053-.42Zm-2.421 10a4.737 4.737 0 0 0 1.052-3c0-1.21-.368-2.21-1.052-3.052a3.316 3.316 0 0 0-2.685-1.21c-1.105 0-1.947.42-2.631 1.21-.684.842-1.053 1.947-1.053 3.052 0 1.21.369 2.21 1 3 .684.79 1.684 1.264 2.684 1.21 1.106 0 2-.42 2.685-1.21Zm16.473 0c.684-.842 1.053-1.947 1.053-3.052 0-1.21-.316-2.21-1-3a3.405 3.405 0 0 0-2.684-1.21c-1.105 0-2 .42-2.684 1.21a4.842 4.842 0 0 0-1.053 3c0 1.21.316 2.21 1.052 3.052.685.79 1.685 1.264 2.685 1.21 1.105 0 1.947-.42 2.631-1.21Zm9.79 2.21c0 .422-.158.79-.421 1.106-.263.316-.684.421-1.105.421a1.47 1.47 0 0 1-1.053-.42c-.263-.317-.421-.685-.421-1.106V7.536c0-.42.158-.79.42-1.105.317-.316.685-.474 1.106-.421.421 0 .79.158 1.053.421.316.316.42.684.42 1.105v15.632Zm14.947-4.525c.316-.264.474-.58.474-1 0-2.369-1.263-4.58-3.105-5.843-1-.579-2.053-.894-3.158-.894-1.158 0-2.21.316-3.21.894a6.563 6.563 0 0 0-2.422 2.527c-.579 1.052-.895 2.316-.895 3.79a6.729 6.729 0 0 0 .843 3.526c.578 1 1.368 1.842 2.42 2.42 1.053.58 2.264.895 3.685.895.842 0 1.631-.158 2.421-.42a6.38 6.38 0 0 0 1.895-1c.473-.316.684-.738.684-1.211 0-.369-.106-.632-.369-.895a1.153 1.153 0 0 0-.894-.368 1.67 1.67 0 0 0-.843.21l-.473.263c-.316.21-.632.316-.948.421-.421.158-.947.21-1.631.21a3.805 3.805 0 0 1-3.842-3.157h8.316c.421 0 .736-.105 1.052-.368Zm-5.947-4.948c.789 0 1.526.263 2.105.737s1 1.105 1.053 1.842v.158h-6.632c.368-1.842 1.526-2.737 3.474-2.737Zm17.21-2.316c-.315-.263-.737-.42-1.263-.42-.789 0-1.579.157-2.263.526-.737.368-1.316.894-1.684 1.579v-.316c0-.421-.105-.79-.421-1.105a1.695 1.695 0 0 0-1.106-.421 1.239 1.239 0 0 0-1.105.42c-.263.316-.421.685-.421 1.106v10.368c0 .421.158.79.421 1.106.316.263.632.42 1.105.42.474 0 .843-.105 1.106-.42.263-.316.421-.685.421-1.106v-6c-.053-.684.105-1.316.368-1.947.211-.474.579-.895 1-1.21.527-.37 1.211-.474 1.842-.316l.316.105c.316.105.579.158.842.158.369.052.684-.106.948-.369.315-.263.421-.631.421-1.158 0-.368-.211-.736-.527-1Z"
/> ></path>
<path <path fill="#066FD1" d="M31.288 7.107A8.833 8.833 0 0 0 24.893.712a55.899 55.899 0 0 0-17.786 0A8.833 8.833 0 0 0 .712 7.107a55.899 55.899 0 0 0 0 17.786 8.833 8.833 0 0 0 6.395 6.395c5.895.95 11.89.95 17.786 0a8.833 8.833 0 0 0 6.395-6.395c.95-5.895.95-11.89 0-17.786Z"></path>
fill="#066FD1" <path
d="M31.288 7.107A8.833 8.833 0 0 0 24.893.712a55.899 55.899 0 0 0-17.786 0A8.833 8.833 0 0 0 .712 7.107a55.899 55.899 0 0 0 0 17.786 8.833 8.833 0 0 0 6.395 6.395c5.895.95 11.89.95 17.786 0a8.833 8.833 0 0 0 6.395-6.395c.95-5.895.95-11.89 0-17.786Z" fill="#fff"
/> d="M10.019 22.452a17.5 17.5 0 0 0 5.498-5.449 2.193 2.193 0 0 0 0-2.328c-1.436-2.18-3.27-4.013-5.498-5.4-1.09-.693-2.527-.297-3.22.793-.05 0-.05.05-.05.05a2.278 2.278 0 0 0 .842 3.12c1.14.743 2.13 1.585 3.022 2.626a11.782 11.782 0 0 1-3.022 2.625c-.743.396-1.189 1.189-1.189 2.031a2.397 2.397 0 0 0 3.617 1.932Zm7.727-.644h5.647c1.04 0 1.883-.495 1.883-1.486 0-.99-.843-1.486-1.883-1.486h-5.647c-1.04 0-1.882.495-1.882 1.486 0 .99.842 1.486 1.882 1.486Z"
<path ></path>
fill="#fff"
d="M10.019 22.452a17.5 17.5 0 0 0 5.498-5.449 2.193 2.193 0 0 0 0-2.328c-1.436-2.18-3.27-4.013-5.498-5.4-1.09-.693-2.527-.297-3.22.793-.05 0-.05.05-.05.05a2.278 2.278 0 0 0 .842 3.12c1.14.743 2.13 1.585 3.022 2.626a11.782 11.782 0 0 1-3.022 2.625c-.743.396-1.189 1.189-1.189 2.031a2.397 2.397 0 0 0 3.617 1.932Zm7.727-.644h5.647c1.04 0 1.883-.495 1.883-1.486 0-.99-.843-1.486-1.883-1.486h-5.647c-1.04 0-1.882.495-1.882 1.486 0 .99.842 1.486 1.882 1.486Z"
/>
</svg> </svg>
+52 -64
View File
@@ -4,87 +4,75 @@
// the tree is frozen in shared/data/docs.json (extracted from the reference // the tree is frozen in shared/data/docs.json (extracted from the reference
// development docs build). // development docs build).
import Subheader from '@ui/Subheader.astro'; import Subheader from '@ui/Subheader.astro'
import docs from '@data/docs.json'; import docs from '@data/docs.json'
import { pathSlug as slug } from '@shared/lib/string-format'; import { pathSlug as slug } from '@shared/lib/string-format'
interface MenuLeaf { interface MenuLeaf {
title: string; title: string
url: string; url: string
} }
interface MenuGroup extends MenuLeaf { interface MenuGroup extends MenuLeaf {
children?: MenuLeaf[]; children?: MenuLeaf[]
} }
interface MenuSection { interface MenuSection {
title: string; title: string
children?: MenuGroup[]; children?: MenuGroup[]
} }
interface Props { interface Props {
/** Page URL in the docs namespace (equivalent of page.url), e.g. "/ui/components/alert/". */ /** Page URL in the docs namespace (equivalent of page.url), e.g. "/ui/components/alert/". */
url: string; url: string
} }
const { url } = Astro.props; const { url } = Astro.props
const menu = docs.menu as MenuSection[]; const menu = docs.menu as MenuSection[]
--- ---
<!-- BEGIN DOCS MENU --> <!-- BEGIN DOCS MENU -->
<nav class="space-y space-y-5" id="menu"> <nav class="space-y space-y-5" id="menu">
{ {
menu.map((level1) => ( menu.map((level1) => (
<div> <div>
<Subheader class="mb-2">{level1.title}</Subheader> <Subheader class="mb-2">{level1.title}</Subheader>
{level1.children && level1.children.length > 0 && ( {level1.children && level1.children.length > 0 && (
<nav class="nav nav-vertical"> <nav class="nav nav-vertical">
{level1.children.map((level2) => { {level1.children.map((level2) => {
const hasChildren = Boolean(level2.children && level2.children.length > 0); const hasChildren = Boolean(level2.children && level2.children.length > 0)
// Expanded only for the group's own page or one of its direct children — // Expanded only for the group's own page or one of its direct children —
// a prefix check would also expand "Introduction" on /ui/getting-started/frameworks/* pages. // a prefix check would also expand "Introduction" on /ui/getting-started/frameworks/* pages.
const expanded = url === level2.url || Boolean(level2.children?.some((child) => child.url === url)); const expanded = url === level2.url || Boolean(level2.children?.some((child) => child.url === url))
return ( return (
<div> <div>
{/* as in Liquid: href/data-bs-* only when the element has children */} {/* as in Liquid: href/data-bs-* only when the element has children */}
{hasChildren ? ( {hasChildren ? (
<a <a class={`nav-link${expanded ? ' active' : ''}`} href={level2.url} data-bs-toggle="collapse" data-bs-target={`#collapse-${slug(level2.url)}`} aria-expanded={expanded ? 'true' : 'false'}>
class={`nav-link${expanded ? ' active' : ''}`} {level2.title} <span class="nav-link-toggle" />
href={level2.url} </a>
data-bs-toggle="collapse" ) : (
data-bs-target={`#collapse-${slug(level2.url)}`} <a class={`nav-link${expanded ? ' active' : ''}`}>{level2.title}</a>
aria-expanded={expanded ? 'true' : 'false'} )}
>
{level2.title} <span class="nav-link-toggle" />
</a>
) : (
<a class={`nav-link${expanded ? ' active' : ''}`}>{level2.title}</a>
)}
{hasChildren && ( {hasChildren && (
<nav <nav class={`nav nav-vertical collapse${expanded ? ' show' : ''}`} id={`collapse-${slug(level2.url)}`}>
class={`nav nav-vertical collapse${expanded ? ' show' : ''}`} {level2.children!.map((level3) => (
id={`collapse-${slug(level2.url)}`} <div>
> <a class={`nav-link${url === level3.url ? ' active' : ''}`} href={level3.url}>
{level2.children!.map((level3) => ( {level3.title}
<div> </a>
<a </div>
class={`nav-link${url === level3.url ? ' active' : ''}`} ))}
href={level3.url} </nav>
> )}
{level3.title} </div>
</a> )
</div> })}
))} </nav>
</nav> )}
)} </div>
</div> ))
); }
})}
</nav>
)}
</div>
))
}
</nav> </nav>
<!-- END DOCS MENU --> <!-- END DOCS MENU -->
+53 -53
View File
@@ -1,63 +1,63 @@
--- ---
// Equivalent of docs/navbar.html. // Equivalent of docs/navbar.html.
import Icon from '@ui/Icon.astro'; import Icon from '@ui/Icon.astro'
import DocsLogo from './DocsLogo.astro'; import DocsLogo from './DocsLogo.astro'
import { site } from '@shared/lib/site'; import { site } from '@shared/lib/site'
// Eventually belongs in src/lib/site.ts (out of scope for this task). // Eventually belongs in src/lib/site.ts (out of scope for this task).
const previewUrl = 'https://preview.tabler.io'; const previewUrl = 'https://preview.tabler.io'
const changelogUrl = 'https://tabler.io/changelog'; const changelogUrl = 'https://tabler.io/changelog'
--- ---
<!-- BEGIN DOCS NAVBAR --> <!-- BEGIN DOCS NAVBAR -->
<div class="navbar navbar-expand sticky-top"> <div class="navbar navbar-expand sticky-top">
<div class="container"> <div class="container">
<div class="row flex-fill align-items-md-center"> <div class="row flex-fill align-items-md-center">
<div class="col"> <div class="col">
<div class="d-flex align-items-center gap-4"> <div class="d-flex align-items-center gap-4">
{/* href=".": Liquid `page.url | relative` receives a string, so the filter always returns "." */} {/* href=".": Liquid `page.url | relative` receives a string, so the filter always returns "." */}
<a href="." class="navbar-brand navbar-brand-autodark gap-4"> <a href="." class="navbar-brand navbar-brand-autodark gap-4">
<DocsLogo /> <DocsLogo />
</a> </a>
<div> <div>
<span class="badge">v{site.version}</span> <span class="badge">v{site.version}</span>
</div> </div>
</div> </div>
</div> </div>
<div class="d-none d-md-block col"> <div class="d-none d-md-block col">
<div id="docsearch"></div> <div id="docsearch"></div>
</div> </div>
<div class="col d-flex"> <div class="col d-flex">
<ul class="navbar-nav ms-auto gap-2 align-items-center"> <ul class="navbar-nav ms-auto gap-2 align-items-center">
<div class="nav-item d-none d-md-block"> <div class="nav-item d-none d-md-block">
<a href={previewUrl} class="nav-link">Preview</a> <a href={previewUrl} class="nav-link">Preview</a>
</div> </div>
<div class="nav-item d-none d-md-block"> <div class="nav-item d-none d-md-block">
<a href={changelogUrl} class="nav-link">Changelog</a> <a href={changelogUrl} class="nav-link">Changelog</a>
</div> </div>
<li class="nav-item hide-theme-dark"> <li class="nav-item hide-theme-dark">
<a href="?theme=dark" class="btn btn-icon"> <a href="?theme=dark" class="btn btn-icon">
<Icon name="moon" /> <Icon name="moon" />
</a> </a>
</li> </li>
<li class="nav-item hide-theme-light"> <li class="nav-item hide-theme-light">
<a href="?theme=light" class="btn btn-icon"> <a href="?theme=light" class="btn btn-icon">
<Icon name="sun" /> <Icon name="sun" />
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a href={site.githubUrl} class="btn btn-icon" target="_blank"> <a href={site.githubUrl} class="btn btn-icon" target="_blank">
<Icon name="brand-github" /> <Icon name="brand-github" />
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a href={previewUrl} class="btn btn-primary" target="_blank"> <a href={previewUrl} class="btn btn-primary" target="_blank">
<Icon name="eye" /> Preview <Icon name="eye" /> Preview
</a> </a>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<!-- END DOCS NAVBAR --> <!-- END DOCS NAVBAR -->
+83 -89
View File
@@ -1,120 +1,114 @@
--- ---
// Equivalent of docs/pagination.html: child-page cards or next/prev siblings. // Equivalent of docs/pagination.html: child-page cards or next/prev siblings.
import DocsCard from './DocsCard.astro'; import DocsCard from './DocsCard.astro'
import docs from '@data/docs.json'; import docs from '@data/docs.json'
import Icon from '@ui/Icon.astro'; import Icon from '@ui/Icon.astro'
import { getDocsChildren } from '@shared/lib/docs-children'; import { getDocsChildren } from '@shared/lib/docs-children'
import type { DocsChildPage } from '@shared/lib/docs-children'; import type { DocsChildPage } from '@shared/lib/docs-children'
interface MenuLeaf { interface MenuLeaf {
title: string; title: string
url: string; url: string
} }
interface MenuNode { interface MenuNode {
title: string; title: string
url?: string; url?: string
children?: MenuNode[]; children?: MenuNode[]
} }
interface Props { interface Props {
/** Page URL in the docs namespace (equivalent of page.url), e.g. "/ui/components/alert/". */ /** Page URL in the docs namespace (equivalent of page.url), e.g. "/ui/components/alert/". */
url: string; url: string
} }
const { url } = Astro.props; const { url } = Astro.props
const children = getDocsChildren(url); const children = getDocsChildren(url)
type Pagination = { type Pagination = {
prev: MenuLeaf | null; prev: MenuLeaf | null
next: MenuLeaf | null; next: MenuLeaf | null
found: boolean; found: boolean
}; }
function findPage(nodes: MenuNode[]): Pagination | null { function findPage(nodes: MenuNode[]): Pagination | null {
const index = nodes.findIndex((item) => item.url === url); const index = nodes.findIndex((item) => item.url === url)
if (index !== -1) { if (index !== -1) {
const previousNode = index > 0 ? nodes[index - 1] : null; const previousNode = index > 0 ? nodes[index - 1] : null
const nextNode = index < nodes.length - 1 ? nodes[index + 1] : null; const nextNode = index < nodes.length - 1 ? nodes[index + 1] : null
return { return {
prev: previousNode?.url ? { title: previousNode.title, url: previousNode.url } : null, prev: previousNode?.url ? { title: previousNode.title, url: previousNode.url } : null,
next: nextNode?.url ? { title: nextNode.title, url: nextNode.url } : null, next: nextNode?.url ? { title: nextNode.title, url: nextNode.url } : null,
found: true, found: true,
}; }
} }
for (const node of nodes) { for (const node of nodes) {
const result = node.children ? findPage(node.children) : null; const result = node.children ? findPage(node.children) : null
if (result) return result; if (result) return result
} }
return null; return null
} }
const { prev, next, found } = findPage(docs.menu as MenuNode[]) ?? { const { prev, next, found } = findPage(docs.menu as MenuNode[]) ?? {
prev: null, prev: null,
next: null, next: null,
found: false, found: false,
}; }
--- ---
<!-- BEGIN DOCS PAGINATION --> <!-- BEGIN DOCS PAGINATION -->{
{ children.length > 0 && (
children.length > 0 && ( <div class="mt-6 pt-6">
<div class="mt-6 pt-6"> <div class="row row-deck row-cards">
<div class="row row-deck row-cards"> {children.map((child: DocsChildPage) => (
{children.map((child: DocsChildPage) => ( <DocsCard href={child.url} title={child.title} description={child.description} icon={child.icon} />
<DocsCard ))}
href={child.url} </div>
title={child.title} </div>
description={child.description} )
icon={child.icon}
/>
))}
</div>
</div>
)
} }
{ {
children.length === 0 && found && ( children.length === 0 && found && (
<div class="mt-6 pt-6"> <div class="mt-6 pt-6">
<ul class="pagination"> <ul class="pagination">
{prev && ( {prev && (
<li class="page-item page-prev"> <li class="page-item page-prev">
<a class="page-link" href={prev.url}> <a class="page-link" href={prev.url}>
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col-auto"> <div class="col-auto">
<Icon name="chevron-left" /> <Icon name="chevron-left" />
</div> </div>
<div class="col"> <div class="col">
<div class="page-item-subtitle">previous</div> <div class="page-item-subtitle">previous</div>
<div class="page-item-title">{prev.title}</div> <div class="page-item-title">{prev.title}</div>
</div> </div>
</div> </div>
</a> </a>
</li> </li>
)} )}
{next && ( {next && (
<li class="page-item page-next"> <li class="page-item page-next">
<a class="page-link" href={next.url}> <a class="page-link" href={next.url}>
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col"> <div class="col">
<div class="page-item-subtitle">next</div> <div class="page-item-subtitle">next</div>
<div class="page-item-title">{next.title}</div> <div class="page-item-title">{next.title}</div>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<Icon name="chevron-right" /> <Icon name="chevron-right" />
</div> </div>
</div> </div>
</a> </a>
</li> </li>
)} )}
</ul> </ul>
</div> </div>
) )
} }
<!-- END DOCS PAGINATION --> <!-- END DOCS PAGINATION -->
+25 -26
View File
@@ -4,44 +4,43 @@
// here it comes in as a prop from the consuming page. // here it comes in as a prop from the consuming page.
export interface TocItem { export interface TocItem {
/** 2 or 3 (h2/h3); h3 gets the ms-3 indent */ /** 2 or 3 (h2/h3); h3 gets the ms-3 indent */
level: number; level: number
text: string; text: string
/** anchor without '#', e.g. "default-markup" */ /** anchor without '#', e.g. "default-markup" */
id: string; id: string
} }
interface Props { interface Props {
toc?: TocItem[]; toc?: TocItem[]
} }
const { toc = [] } = Astro.props; const { toc = [] } = Astro.props
// Equivalent of `illustrations | size` (shared/data/illustrations.json has 100 entries). // Equivalent of `illustrations | size` (shared/data/illustrations.json has 100 entries).
const illustrationsCount = 100; const illustrationsCount = 100
--- ---
<!-- BEGIN DOCS TOC --> <!-- BEGIN DOCS TOC -->{
{ toc.length > 0 && (
toc.length > 0 && ( <Fragment>
<Fragment> <h3>Table of Contents</h3>
<h3>Table of Contents</h3> <div class="nav nav-vertical" id="toc">
<div class="nav nav-vertical" id="toc"> {toc.map((item) => (
{toc.map((item) => ( <a href={`#${item.id}`} class={`nav-link${item.level === 3 ? ' ms-3' : ''}`}>
<a href={`#${item.id}`} class={`nav-link${item.level === 3 ? ' ms-3' : ''}`}> {item.text}
{item.text} </a>
</a> ))}
))} </div>
</div> </Fragment>
</Fragment> )
)
} }
<a href="https://r.tabler.io/illustrations" class="card card-sm mt-6 shadow-none" target="_blank"> <a href="https://r.tabler.io/illustrations" class="card card-sm mt-6 shadow-none" target="_blank">
<div class="card-body"> <div class="card-body">
<img src="/img/banner-carbon.png" class="mb-3" alt="" /> <img src="/img/banner-carbon.png" class="mb-3" alt="" />
<h4>{illustrationsCount} sleek illustrations for your startup's visual identity.</h4> <h4>{illustrationsCount} sleek illustrations for your startup's visual identity.</h4>
</div> </div>
</a> </a>
<!-- END DOCS TOC --> <!-- END DOCS TOC -->
+5 -5
View File
@@ -1,12 +1,12 @@
--- ---
interface Props { interface Props {
href: string href: string
title: string title: string
} }
const { href, title }: Props = Astro.props; const { href, title }: Props = Astro.props
--- ---
<div class="my-6 text-center"> <div class="my-6 text-center">
<a href={href} class="btn btn btn-primary" target="_blank">{title}</a> <a href={href} class="btn btn btn-primary" target="_blank">{title}</a>
</div> </div>
+45 -76
View File
@@ -1,99 +1,68 @@
--- ---
// copy button with data-clipboard-text (equivalent of escape_attribute). // copy button with data-clipboard-text (equivalent of escape_attribute).
// The example code comes from the `html` prop or from the rendered slot. // The example code comes from the `html` prop or from the rendered slot.
import Icon from '@ui/Icon.astro'; import Icon from '@ui/Icon.astro'
import { beautifyHtml, highlightCode, removeHref } from '@shared/lib/code-example'; import { beautifyHtml, highlightCode, removeHref } from '@shared/lib/code-example'
interface Props { interface Props {
/** raw HTML of the example; when absent — the slot is rendered */ /** raw HTML of the example; when absent — the slot is rendered */
html?: string; html?: string
/** code shown in the panel instead of html (the code param in Liquid) */ /** code shown in the panel instead of html (the code param in Liquid) */
code?: string; code?: string
raw?: boolean; raw?: boolean
overflow?: string; overflow?: string
bg?: string; bg?: string
class?: string; class?: string
height?: string; height?: string
column?: boolean; column?: boolean
centered?: boolean; centered?: boolean
vertical?: boolean; vertical?: boolean
columnFullWidth?: boolean; columnFullWidth?: boolean
hideCode?: boolean; hideCode?: boolean
codeOnly?: boolean; codeOnly?: boolean
} }
const { const { html: htmlProp, code, raw, overflow = 'auto', bg, class: className, height, column, centered, vertical, columnFullWidth, hideCode, codeOnly } = Astro.props
html: htmlProp,
code,
raw,
overflow = 'auto',
bg,
class: className,
height,
column,
centered,
vertical,
columnFullWidth,
hideCode,
codeOnly,
} = Astro.props;
// equivalent of {% removeemptylines %} around include output (e.g. ui/alert.html) // equivalent of {% removeemptylines %} around include output (e.g. ui/alert.html)
let html = (htmlProp ?? (await Astro.slots.render('default'))).replace(/^\s*[\r\n]/gm, ''); let html = (htmlProp ?? (await Astro.slots.render('default'))).replace(/^\s*[\r\n]/gm, '')
// JSX reserializes the slot markup: empty SVG elements get explicit closing // JSX reserializes the slot markup: empty SVG elements get explicit closing
// tags, and apostrophes in text are escaped. We restore the source form so the // tags, and apostrophes in text are escaped. We restore the source form so the
// code in the panel looks like hand-written HTML. // code in the panel looks like hand-written HTML.
html = html html = html.replace(/<(path|circle|line|polyline|polygon|rect|ellipse)([^>]*?)\s*><\/\1>/g, '<$1$2 />').replace(/&#39;/g, "'")
.replace(/<(path|circle|line|polyline|polygon|rect|ellipse)([^>]*?)\s*><\/\1>/g, '<$1$2 />')
.replace(/&#39;/g, "'");
const exampleClasses = [ const exampleClasses = ['example fs-base border rounded my-5', !raw && 'd-flex flex-wrap justify-content-center', `overflow-${overflow}`, 'position-relative', bg ? `bg-${bg}` : 'bg-pattern-rectangles', className]
'example fs-base border rounded my-5',
!raw && 'd-flex flex-wrap justify-content-center',
`overflow-${overflow}`,
'position-relative',
bg ? `bg-${bg}` : 'bg-pattern-rectangles',
className,
]
const innerClasses = [ const innerClasses = ['p-6 w-full', column && 'd-flex gap-3 flex-column', !column && centered && `d-flex flex-fill flex-wrap gap-2 justify-content-center${vertical ? ' align-items-center flex-column' : ' justify-content-center'}`, columnFullWidth && 'd-flex flex-fill flex-column gap-2']
'p-6 w-full',
column && 'd-flex gap-3 flex-column',
!column && centered && `d-flex flex-fill flex-wrap gap-2 justify-content-center${vertical ? ' align-items-center flex-column' : ' justify-content-center'}`,
columnFullWidth && 'd-flex flex-fill flex-column gap-2',
]
const highlighted = hideCode ? '' : await highlightCode(beautifyHtml(code ?? html), 'html'); const highlighted = hideCode ? '' : await highlightCode(beautifyHtml(code ?? html), 'html')
--- ---
<!--EXAMPLE--> <!--EXAMPLE-->{
{ !codeOnly && (
!codeOnly && ( <div class:list={exampleClasses} style={height ? `height: ${height}` : undefined}>
<div class:list={exampleClasses} style={height ? `height: ${height}` : undefined}> {raw ? (
{ <Fragment set:html={removeHref(html)} />
raw ? ( ) : (
<Fragment set:html={removeHref(html)} /> <div class:list={innerClasses} style={column ? 'max-width: 25rem;' : undefined}>
) : ( <Fragment set:html={removeHref(html)} />
<div class:list={innerClasses} style={column ? 'max-width: 25rem;' : undefined}> </div>
<Fragment set:html={removeHref(html)} /> )}
</div> </div>
) )
}
</div>
)
} }
{ {
!hideCode && ( !hideCode && (
<div class="position-relative"> <div class="position-relative">
{/* Astro escapes the attribute value itself (equivalent of escape_attribute) */} {/* Astro escapes the attribute value itself (equivalent of escape_attribute) */}
<a class="btn btn-icon btn-dark position-absolute m-2 top-0 end-0 z-3" data-clipboard-text={html}> <a class="btn btn-icon btn-dark position-absolute m-2 top-0 end-0 z-3" data-clipboard-text={html}>
<Icon name="clipboard" /> <Icon name="clipboard" />
<Icon name="check" class="d-none" /> <Icon name="check" class="d-none" />
</a> </a>
<Fragment set:html={highlighted} /> <Fragment set:html={highlighted} />
</div> </div>
) )
} }
<!--/EXAMPLE--> <!--/EXAMPLE-->
+24 -18
View File
@@ -3,24 +3,30 @@ import flags from '@data/flags.json'
--- ---
<table class="table table-vcenter"> <table class="table table-vcenter">
<thead> <thead>
<tr> <tr>
<th class="w-1">Flag</th> <th class="w-1">Flag</th>
<th>Name</th> <th>Name</th>
<th class="w-1">Country code</th> <th class="w-1">Country code</th>
<th>CSS class</th> <th>CSS class</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{flags.map((flag) => ( {
<tr> flags.map((flag) => (
<td> <tr>
<div class={`flag flag-sm flag-country-${ flag.flag }`}></div> <td>
</td> <div class={`flag flag-sm flag-country-${flag.flag}`} />
<td>{ flag.name }</td> </td>
<td><code>{ flag.flag }</code></td> <td>{flag.name}</td>
<td><code>.flag-country-{ flag.flag }</code></td> <td>
</tr> <code>{flag.flag}</code>
))} </td>
<td>
<code>.flag-country-{flag.flag}</code>
</td>
</tr>
))
}
</tbody> </tbody>
</table> </table>
+22 -26
View File
@@ -1,30 +1,26 @@
--- ---
import openSources from '@data/open-source.json'; import openSources from '@data/open-source.json'
--- ---
<table class="table"> <table class="table">
<tr> <tr>
<th> <th> Name </th>
Name <th> Description </th>
</th> <th> License </th>
<th> </tr>
Description {
</th> openSources.map((el) => {
<th> return (
License <tr>
</th> <td>
</tr> <a href={el.url} target="_blank">
{ openSources.map((el) => { {el.name}
return ( </a>
<tr> </td>
<td> <td>{el.description}</td>
<a href={el.url} target="_blank"> <td>{el.license}</td>
{el.name} </tr>
</a> )
</td> })
<td>{el.description}</td> }
<td>{el.license}</td> </table>
</tr>
)
}) }
</table>
+31 -26
View File
@@ -1,33 +1,38 @@
--- ---
import payments from '@data/payments.json'; import payments from '@data/payments.json'
--- ---
<table class="table table-vcenter"> <table class="table table-vcenter">
<thead> <thead>
<tr> <tr>
<th class="w-1">Icon light</th> <th class="w-1">Icon light</th>
<th class="w-1">Icon dark</th> <th class="w-1">Icon dark</th>
<th>Provider name</th> <th>Provider name</th>
<th>CSS class</th> <th>CSS class</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{payments.map((payment) => { {
return ( payments.map((payment) => {
<tr> return (
<td> <tr>
<div class={`payment payment-sm payment-provider-${ payment.logo }`}></div> <td>
</td> <div class={`payment payment-sm payment-provider-${payment.logo}`} />
<td> </td>
<div class={`payment payment-sm payment-provider-${ payment.logo }-dark`}></div> <td>
</td> <div class={`payment payment-sm payment-provider-${payment.logo}-dark`} />
<td>{ payment.name }</td> </td>
<td> <td>{payment.name}</td>
<code>.payment-provider-{ payment.logo }</code><br> <td>
<code>.payment-provider-{ payment.logo }-dark</code> <>
</td> <code>.payment-provider-{payment.logo}</code>
</tr> <br />
); </>
})} <code>.payment-provider-{payment.logo}-dark</code>
</td>
</tr>
)
})
}
</tbody> </tbody>
</table> </table>
+24 -19
View File
@@ -1,26 +1,31 @@
--- ---
import socials from '@data/socials.json'; import socials from '@data/socials.json'
--- ---
<table class="table table-vcenter"> <table class="table table-vcenter">
<thead> <thead>
<tr> <tr>
<th class="w-1">App</th> <th class="w-1">App</th>
<th>Name</th> <th>Name</th>
<th>CSS class</th> <th>CSS class</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{socials.map((social) => ( {
<tr> socials.map((social) => (
<td> <tr>
<div class={`social social-sm social-app-${ social.file }`}></div> <td>
</td> <div class={`social social-sm social-app-${social.file}`} />
<td>{ social.name }</td> </td>
<td> <td>{social.name}</td>
<code>.social-app-{ social.file }</code><br> <td>
</td> <>
</tr> <code>.social-app-{social.file}</code>
))} <br />
</>
</td>
</tr>
))
}
</tbody> </tbody>
</table> </table>
+1 -1
View File
@@ -4,5 +4,5 @@
--- ---
<div class="steps steps-vertical"> <div class="steps steps-vertical">
<slot /> <slot />
</div> </div>
+25 -34
View File
@@ -1,45 +1,36 @@
--- ---
import { Code } from 'astro:components'; import { Code } from 'astro:components'
import packageManagers from '@data/package-managers.json'; import packageManagers from '@data/package-managers.json'
interface Props { interface Props {
name: string; name: string
} }
const { name }: Props = Astro.props; const { name }: Props = Astro.props
--- ---
<ul class="nav nav-bordered mb-2" role="tablist"> <ul class="nav nav-bordered mb-2" role="tablist">
{packageManagers.map((manager, i) => { {
return ( packageManagers.map((manager, i) => {
<li class="nav-item" role="presentation"> return (
<button <li class="nav-item" role="presentation">
class={`nav-link${i === 0 ? ' active' : ''}`} <button class={`nav-link${i === 0 ? ' active' : ''}`} id={`${manager.name}-tab`} data-bs-toggle="tab" data-bs-target={`#${manager.name}`} type="button" role="tab" aria-controls="home" aria-selected={i === 0 ? 'true' : 'false'}>
id={`${manager.name}-tab`} <span set:html={manager.icon.replace('<svg ', '<svg class="me-2" ')} />
data-bs-toggle="tab" {manager.name}
data-bs-target={`#${manager.name}`} </button>
type="button" </li>
role="tab" )
aria-controls="home" })
aria-selected={i === 0 ? 'true' : 'false'}> }
<span set:html={manager.icon.replace('<svg ', '<svg class="me-2" ')} />
{manager.name}
</button>
</li>
);
})}
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
{packageManagers.map((manager, i) => { {
return ( packageManagers.map((manager, i) => {
<div return (
class={`tab-pane${i === 0 ? ' show active' : ''}`} <div class={`tab-pane${i === 0 ? ' show active' : ''}`} id={`${manager.name}`} role="tabpanel" aria-labelledby={`${manager.name}-tab`}>
id={`${manager.name}`} <Code code={`${manager.command} ${manager.install} ${name}`} lang="bash" />
role="tabpanel" </div>
aria-labelledby={`${manager.name}-tab`} )
> })
<Code code={`${manager.command} ${manager.install} ${name}`} lang="bash" /> }
</div>
);
})}
</div> </div>
File renamed without changes.
+65
View File
@@ -0,0 +1,65 @@
---
// Adapter: MDX (layout in frontmatter) → DocsLayout.
// Astro passes { frontmatter, headings, url, file } to the MDX layout — we map it
// onto DocsLayout props, computing the TOC from the headings (equivalent of the `toc` filter).
import DocsLayout from './DocsLayout.astro'
interface MdxHeading {
depth: number
slug: string
text: string
}
interface MdxFrontmatter {
'title'?: string
'seoTitle'?: string
'summary'?: string
'description'?: string
'seoDescription'?: string
'added-in'?: string;
'docs-libs'?: string[]
'hide-pagination'?: boolean
}
const {
frontmatter,
headings = [],
url,
file,
} = Astro.props as {
frontmatter: MdxFrontmatter
headings: MdxHeading[]
url?: string
file?: string
}
// Repo-relative source path for the "Edit this page" link, e.g.
// "docs/pages/ui/components/alert.mdx" (`file` is the absolute path of the MDX page).
const editPath = file?.includes('/docs/pages/') ? file.replace(/^.*\/docs\/pages\//, 'docs/pages/') : undefined
const docsLibs = frontmatter['docs-libs'] ?? []
const toc = headings
.filter((heading) => heading.depth === 2 || heading.depth === 3)
.map((heading) => ({
level: heading.depth as 2 | 3,
text: heading.text,
id: heading.slug,
}))
---
<DocsLayout
title={frontmatter.title ?? ''}
seoTitle={frontmatter.seoTitle}
summary={frontmatter.summary}
description={frontmatter.description}
seoDescription={frontmatter.seoDescription}
addedIn={frontmatter['added-in']}
docsLibs={docsLibs}
hidePagination={frontmatter['hide-pagination']}
editPath={editPath}
toc={toc}
url={url ? `${url.replace(/\.html$/, '').replace(/\/$/, '')}/` : undefined}
>
<slot />
</DocsLayout>
+1 -1
View File
@@ -5,5 +5,5 @@ order: 4
description: Customizable email templates for over 90 clients and devices. description: Customizable email templates for over 90 clients and devices.
summary: Tabler Emails is a set of 80 eye-catching, customizable HTML templates. They are compatible with over 90 email clients and devices. summary: Tabler Emails is a set of 80 eye-catching, customizable HTML templates. They are compatible with over 90 email clients and devices.
seoDescription: Tabler Emails is a collection of 80 premium, customizable HTML templates. They are compatible with over 90 email clients and devices. seoDescription: Tabler Emails is a collection of 80 premium, customizable HTML templates. They are compatible with over 90 email clients and devices.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
@@ -5,7 +5,7 @@ seoTitle: Tabler Emails - How to use the compiled HTML email templates
description: Learn how to use the compiled HTML email templates from the Tabler Emails package. description: Learn how to use the compiled HTML email templates from the Tabler Emails package.
summary: The compiled HTML files from the Tabler Emails package are ready to use in your email marketing campaigns. This guide explains how to use them effectively. summary: The compiled HTML files from the Tabler Emails package are ready to use in your email marketing campaigns. This guide explains how to use them effectively.
seoDescription: The compiled HTML files from the Tabler Emails package are ready to use in your email marketing campaigns. This guide explains how to use them effectively. seoDescription: The compiled HTML files from the Tabler Emails package are ready to use in your email marketing campaigns. This guide explains how to use them effectively.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -5,7 +5,7 @@ seoTitle: Tabler Emails - Content of the package
description: Content of the Tabler Emails package. description: Content of the Tabler Emails package.
summary: The Tabler Emails package contains files which can be used by everyone, even without great knowledge of HTML. summary: The Tabler Emails package contains files which can be used by everyone, even without great knowledge of HTML.
seoDescription: todo seoDescription: todo
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -4,5 +4,5 @@ seoTitle: Introduction to Tabler Emails
description: Base information about Tabler Emails package. description: Base information about Tabler Emails package.
summary: Tabler Emails is a set of 80 eye-catching, customizable HTML templates. They are compatible with over 90 email clients and devices. summary: Tabler Emails is a set of 80 eye-catching, customizable HTML templates. They are compatible with over 90 email clients and devices.
seoDescription: Tabler Emails is a collection of 80 premium, customizable HTML templates. They are compatible with over 90 email clients and devices. seoDescription: Tabler Emails is a collection of 80 premium, customizable HTML templates. They are compatible with over 90 email clients and devices.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
@@ -5,7 +5,7 @@ seoTitle: Tabler Emails - How to use the source HTML email templates
description: Learn how to use the source HTML email templates from the Tabler Emails package. description: Learn how to use the source HTML email templates from the Tabler Emails package.
summary: The source HTML files from the Tabler Emails package need a bit more work than the compiled ones. Learn how to use them. summary: The source HTML files from the Tabler Emails package need a bit more work than the compiled ones. Learn how to use them.
seoDescription: The source HTML files from the Tabler Emails package need a bit more work than the compiled ones. Learn how to use them. seoDescription: The source HTML files from the Tabler Emails package need a bit more work than the compiled ones. Learn how to use them.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -5,7 +5,7 @@ summary:
developers looking to enhance their projects with high-quality icons. developers looking to enhance their projects with high-quality icons.
order: 2 order: 2
description: Over 5000 pixel-perfect icons for web design and development description: Over 5000 pixel-perfect icons for web design and development
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import { site } from '@shared/lib/site' import { site } from '@shared/lib/site'
+1 -1
View File
@@ -2,7 +2,7 @@
title: Astro title: Astro
description: Tabler Icons library for Astro framework. description: Tabler Icons library for Astro framework.
summary: Tabler Icons for Astro provides an optimized collection of icons specifically designed for use with Astro. These lightweight and scalable icons are easy to integrate into Astro-based projects. summary: Tabler Icons for Astro provides an optimized collection of icons specifically designed for use with Astro. These lightweight and scalable icons are easy to integrate into Astro-based projects.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import TabsPackage from '@components/TabsPackage.astro' import TabsPackage from '@components/TabsPackage.astro'
+1 -1
View File
@@ -2,5 +2,5 @@
title: Libraries title: Libraries
description: Explore Tabler Icons libraries. description: Explore Tabler Icons libraries.
summary: The libraries section offers various integrations of Tabler Icons for popular frameworks and technologies, making it easy to incorporate icons into any project. summary: The libraries section offers various integrations of Tabler Icons for popular frameworks and technologies, making it easy to incorporate icons into any project.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
+1 -1
View File
@@ -2,7 +2,7 @@
title: Preact title: Preact
description: Tabler Icons library for Preact framework. description: Tabler Icons library for Preact framework.
summary: Tabler Icons for Preact provides an optimized collection of icons specifically designed for use with Preact. These lightweight and scalable icons are easy to integrate into Preact-based projects. summary: Tabler Icons for Preact provides an optimized collection of icons specifically designed for use with Preact. These lightweight and scalable icons are easy to integrate into Preact-based projects.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import TabsPackage from '@components/TabsPackage.astro' import TabsPackage from '@components/TabsPackage.astro'
+1 -1
View File
@@ -2,7 +2,7 @@
title: React title: React
description: Tabler Icons library for React framework. description: Tabler Icons library for React framework.
summary: Tabler Icons for React offers a robust set of icons tailored for React applications, providing developers with a seamless way to enhance their user interfaces with high-quality, scalable graphics. summary: Tabler Icons for React offers a robust set of icons tailored for React applications, providing developers with a seamless way to enhance their user interfaces with high-quality, scalable graphics.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import TabsPackage from '@components/TabsPackage.astro' import TabsPackage from '@components/TabsPackage.astro'
+1 -1
View File
@@ -2,7 +2,7 @@
title: SolidJS title: SolidJS
description: Tabler Icons library for SolidJS framework. description: Tabler Icons library for SolidJS framework.
summary: Tabler Icons for SolidJS is a lightweight library offering a vast selection of high-quality icons. It is designed for seamless integration with SolidJS, enabling developers to build visually appealing interfaces. summary: Tabler Icons for SolidJS is a lightweight library offering a vast selection of high-quality icons. It is designed for seamless integration with SolidJS, enabling developers to build visually appealing interfaces.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import TabsPackage from '@components/TabsPackage.astro' import TabsPackage from '@components/TabsPackage.astro'
+1 -1
View File
@@ -2,7 +2,7 @@
title: Svelte title: Svelte
description: Tabler Icons library for Svelte framework. description: Tabler Icons library for Svelte framework.
summary: Tabler Icons for Svelte provides a clean and efficient way to use Tabler's comprehensive icon set in Svelte applications, helping developers deliver polished, user-friendly designs. summary: Tabler Icons for Svelte provides a clean and efficient way to use Tabler's comprehensive icon set in Svelte applications, helping developers deliver polished, user-friendly designs.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import TabsPackage from '@components/TabsPackage.astro' import TabsPackage from '@components/TabsPackage.astro'
+1 -1
View File
@@ -2,7 +2,7 @@
title: Vue title: Vue
description: Tabler Icons library for Vue framework. description: Tabler Icons library for Vue framework.
summary: Tabler Icons for Vue offers a collection of customizable and scalable icons designed for use in Vue applications, providing a powerful tool for creating modern and engaging interfaces. summary: Tabler Icons for Vue offers a collection of customizable and scalable icons designed for use in Vue applications, providing a powerful tool for creating modern and engaging interfaces.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import TabsPackage from '@components/TabsPackage.astro' import TabsPackage from '@components/TabsPackage.astro'
+1 -1
View File
@@ -2,7 +2,7 @@
title: Webfont title: Webfont
description: Tabler Icons as a webfont. description: Tabler Icons as a webfont.
summary: Tabler Icons as a webfont allows you to easily include icons in your projects using simple CSS classes, offering a lightweight and scalable solution for web development. summary: Tabler Icons as a webfont allows you to easily include icons in your projects using simple CSS classes, offering a lightweight and scalable solution for web development.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import TabsPackage from '@components/TabsPackage.astro' import TabsPackage from '@components/TabsPackage.astro'
+1 -1
View File
@@ -2,7 +2,7 @@
title: Figma plugin title: Figma plugin
description: Use Tabler Icons directly in Figma. description: Use Tabler Icons directly in Figma.
summary: The Tabler Figma plugin allows designers to seamlessly integrate Tabler Icons into their Figma projects, providing quick access to a vast library of customizable icons that enhance the design workflow. summary: The Tabler Figma plugin allows designers to seamlessly integrate Tabler Icons into their Figma projects, providing quick access to a vast library of customizable icons that enhance the design workflow.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import DownloadButton from '@components/DownloadButton.astro' import DownloadButton from '@components/DownloadButton.astro'
+1 -1
View File
@@ -1,5 +1,5 @@
--- ---
title: Plugins title: Plugins
description: Icon plugins for seamless integration. description: Icon plugins for seamless integration.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
+1 -1
View File
@@ -1,7 +1,7 @@
--- ---
title: EPS version title: EPS version
description: Download Tabler Icons in EPS format. description: Download Tabler Icons in EPS format.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import TabsPackage from '@components/TabsPackage.astro' import TabsPackage from '@components/TabsPackage.astro'
+1 -1
View File
@@ -2,5 +2,5 @@
title: Static files title: Static files
description: Download static file formats of icons. description: Download static file formats of icons.
summary: Static files provide multiple formats of Tabler Icons, including EPS, PDF, PNG, and SVG, offering flexibility for different design and development workflows. summary: Static files provide multiple formats of Tabler Icons, including EPS, PDF, PNG, and SVG, offering flexibility for different design and development workflows.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
+1 -1
View File
@@ -1,7 +1,7 @@
--- ---
title: PDF version title: PDF version
description: Download Tabler Icons in PDF format. description: Download Tabler Icons in PDF format.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import TabsPackage from '@components/TabsPackage.astro' import TabsPackage from '@components/TabsPackage.astro'
+1 -1
View File
@@ -1,7 +1,7 @@
--- ---
title: PNG version title: PNG version
description: Download Tabler Icons in PNG format. description: Download Tabler Icons in PNG format.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import TabsPackage from '@components/TabsPackage.astro' import TabsPackage from '@components/TabsPackage.astro'
+1 -1
View File
@@ -1,7 +1,7 @@
--- ---
title: SVG version title: SVG version
description: Download Tabler Icons in SVG format. description: Download Tabler Icons in SVG format.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import TabsPackage from '@components/TabsPackage.astro' import TabsPackage from '@components/TabsPackage.astro'
+1 -1
View File
@@ -3,7 +3,7 @@ title: Tabler Illustrations
order: 3 order: 3
description: Customizable illustrations for modern web and mobile designs. description: Customizable illustrations for modern web and mobile designs.
summary: Tabler Illustrations is a collection of customizable SVG illustrations for your web project. Explore our library of illustrations to enhance your web development experience. summary: Tabler Illustrations is a collection of customizable SVG illustrations for your web project. Explore our library of illustrations to enhance your web development experience.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
# Tabler Illustrations # Tabler Illustrations
@@ -2,7 +2,7 @@
title: Contents title: Contents
description: Explore Tabler Illustrations folder structure description: Explore Tabler Illustrations folder structure
summary: The Tabler Illustrations package is thoughtfully structured to provide designers and developers with an array of high-quality assets. This guide explores the various folders and their contents, helping users make the most of these resources. summary: The Tabler Illustrations package is thoughtfully structured to provide designers and developers with an array of high-quality assets. This guide explores the various folders and their contents, helping users make the most of these resources.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
@@ -2,7 +2,7 @@
title: Customization title: Customization
description: Customize the illustrations to match your brand. description: Customize the illustrations to match your brand.
summary: Learn how to tailor Tabler Illustrations by adjusting colors, sizes, and formats. This section provides insights into seamlessly integrating illustrations to align with your design and branding. summary: Learn how to tailor Tabler Illustrations by adjusting colors, sizes, and formats. This section provides insights into seamlessly integrating illustrations to align with your design and branding.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
@@ -2,5 +2,5 @@
title: Introduction title: Introduction
description: Introduction to Tabler Illustrations and their key features. description: Introduction to Tabler Illustrations and their key features.
summary: Tabler Illustrations is a collection of high-quality, customizable illustrations designed to enhance the visual appeal of your projects. These illustrations align seamlessly with the Tabler design system, making it easy to create engaging and cohesive designs for websites, apps, and presentations summary: Tabler Illustrations is a collection of high-quality, customizable illustrations designed to enhance the visual appeal of your projects. These illustrations align seamlessly with the Tabler design system, making it easy to create engaging and cohesive designs for websites, apps, and presentations
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
@@ -1,6 +1,6 @@
--- ---
title: Tabler Illustrations License title: Tabler Illustrations License
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
@@ -1,7 +1,7 @@
--- ---
title: Preview title: Preview
summary: 'Tabler Illustrations offers 80 illustrations in two themes: light and dark. You can use them in your projects to enhance the visual appeal and convey messages effectively.' summary: 'Tabler Illustrations offers 80 illustrations in two themes: light and dark. You can use them in your projects to enhance the visual appeal and convey messages effectively.'
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import illustrations from '@data/illustrations.json' import illustrations from '@data/illustrations.json'
+1 -1
View File
@@ -2,7 +2,7 @@
title: Welcome to Tabler Documentation title: Welcome to Tabler Documentation
summary: Tabler Docs provides a comprehensive guide to help you get started with the Tabler ecosystem, including its UI components, plugins, and icons. Explore detailed documentation to understand and leverage the full potential of Tabler in your projects. summary: Tabler Docs provides a comprehensive guide to help you get started with the Tabler ecosystem, including its UI components, plugins, and icons. Explore detailed documentation to understand and leverage the full potential of Tabler in your projects.
description: Find comprehensive guides, examples, and resources to help you use Tabler UI components, plugins, icons, and tools effectively in your projects. description: Find comprehensive guides, examples, and resources to help you use Tabler UI components, plugins, icons, and tools effectively in your projects.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Card from '@components/DocsCard.astro' import Card from '@components/DocsCard.astro'
+1 -1
View File
@@ -4,7 +4,7 @@ summary:
The choice of colors for a website or app interface has a big influence on how users interact with the product and what decisions they make. Harmonious colors can contribute to a nice first impression and encourage users to engage with your product, so it's a very important aspect of a successful design, which needs The choice of colors for a website or app interface has a big influence on how users interact with the product and what decisions they make. Harmonious colors can contribute to a nice first impression and encourage users to engage with your product, so it's a very important aspect of a successful design, which needs
to be well thought out. to be well thought out.
description: Impact of colors on user interface design. description: Impact of colors on user interface design.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Colors from '@components/Colors.astro' import Colors from '@components/Colors.astro'
+1 -1
View File
@@ -3,5 +3,5 @@ title: Base
order: 2 order: 2
description: Foundational elements for UI design. description: Foundational elements for UI design.
summary: The base section includes foundational elements such as colors, typography, and spacing that form the building blocks of a cohesive and consistent user interface. summary: The base section includes foundational elements such as colors, typography, and spacing that form the building blocks of a cohesive and consistent user interface.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
+1 -1
View File
@@ -4,7 +4,7 @@
// so the include gets an empty url and the target collapses to `{{ page | relative }}`. // so the include gets an empty url and the target collapses to `{{ page | relative }}`.
// For this 3-deep page that is "../../..", which is exactly what the reference // For this 3-deep page that is "../../..", which is exactly what the reference
// build (ui/base/markdown/index.html) redirects to. // build (ui/base/markdown/index.html) redirects to.
import RedirectLayout from '@shared/layouts/RedirectLayout.astro'; import RedirectLayout from '@shared/layouts/RedirectLayout.astro'
--- ---
<RedirectLayout base="../../.." /> <RedirectLayout base="../../.." />
+1 -1
View File
@@ -2,7 +2,7 @@
title: Prose title: Prose
summary: Use the `.prose` wrapper to style long-form content without adding classes to every element. summary: Use the `.prose` wrapper to style long-form content without adding classes to every element.
description: Long-form content styles for markdown-like HTML. description: Long-form content styles for markdown-like HTML.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import Prose from '@ui/Prose.astro'; import Prose from '@ui/Prose.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Typography title: Typography
summary: Typography plays an important role in creating an attractive and clear interface design. Good typography will make the content easy to follow and improve the usability of your website. summary: Typography plays an important role in creating an attractive and clear interface design. Good typography will make the content easy to follow and improve the usability of your website.
description: Role of typography in interface design. description: Role of typography in interface design.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import Prose from '@ui/Prose.astro'; import Prose from '@ui/Prose.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Accordion title: Accordion
summary: Use accordion panels to group related content and show one section at a time. summary: Use accordion panels to group related content and show one section at a time.
description: Build collapsible content sections with accordion classes and modifiers. description: Build collapsible content sections with accordion classes and modifiers.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import Icon from '@ui/Icon.astro'; import Icon from '@ui/Icon.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Alert title: Alert
summary: An alert message is used to inform users about the status of their action and help them solve problems that may occur. Good alert design is important for the overall user experience of a website or app. summary: An alert message is used to inform users about the status of their action and help them solve problems that may occur. Good alert design is important for the overall user experience of a website or app.
description: An alert message for user notification. description: An alert message for user notification.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import Alert from '@ui/Alert.astro'; import Alert from '@ui/Alert.astro';
+1 -1
View File
@@ -3,7 +3,7 @@ title: Autosize
summary: The autosize element will automatically adjust the textarea height and make it easier for users to follow as they type. summary: The autosize element will automatically adjust the textarea height and make it easier for users to follow as they type.
docs-libs: autosize docs-libs: autosize
description: Auto-adjusting textarea for better usability. description: Auto-adjusting textarea for better usability.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Avatar title: Avatar
summary: An avatar helps customize interface elements and make the product experience more personalized. It is often used in communication apps, collaboration tools, and social media. summary: An avatar helps customize interface elements and make the product experience more personalized. It is often used in communication apps, collaboration tools, and social media.
description: Personalize UI with a user avatar. description: Personalize UI with a user avatar.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import Icon from '@ui/Icon.astro'; import Icon from '@ui/Icon.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Badge title: Badge
summary: A badge is a small count and labeling component used to add extra information to an interface element. You can use it to draw user attention to a new element, notify about unread messages, or provide additional context. summary: A badge is a small count and labeling component used to add extra information to an interface element. You can use it to draw user attention to a new element, notify about unread messages, or provide additional context.
description: Add extra information with a badge. description: Add extra information with a badge.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import BadgesList from '@ui/BadgesList.astro'; import BadgesList from '@ui/BadgesList.astro';
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Breadcrumb title: Breadcrumb
summary: A breadcrumb is used to show the current website or app location and reduce the number of actions users need to take. It helps users navigate the website hierarchy and better understand its structure. summary: A breadcrumb is used to show the current website or app location and reduce the number of actions users need to take. It helps users navigate the website hierarchy and better understand its structure.
description: A navigation aid for better structure. description: A navigation aid for better structure.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import Breadcrumb from '@ui/Breadcrumb.astro'; import Breadcrumb from '@ui/Breadcrumb.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Button title: Button
summary: Use a button style that best suits your design and encourages users to take the desired action. You can customize button properties to improve user experience by changing size, shape, color, and more. summary: Use a button style that best suits your design and encourages users to take the desired action. You can customize button properties to improve user experience by changing size, shape, color, and more.
description: A customizable button for user actions. description: A customizable button for user actions.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import ButtonGroup from '@ui/ButtonGroup.astro'; import ButtonGroup from '@ui/ButtonGroup.astro';
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Card gradient title: Card gradient
summary: Card gradients add rich color backgrounds to cards and help emphasize key information in dashboards and marketing sections. summary: Card gradients add rich color backgrounds to cards and help emphasize key information in dashboards and marketing sections.
description: Build eye-catching cards with gradient variants, directions, and animated backgrounds. description: Build eye-catching cards with gradient variants, directions, and animated backgrounds.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro' import Example from '@components/Example.astro'
+1 -1
View File
@@ -2,7 +2,7 @@
title: Card title: Card
summary: A card is a flexible user interface element that helps organize content into meaningful sections and display it across different screen sizes. It can contain smaller elements such as images, text, links, and buttons, and can act as an entry point to more detailed information. summary: A card is a flexible user interface element that helps organize content into meaningful sections and display it across different screen sizes. It can contain smaller elements such as images, text, links, and buttons, and can act as an entry point to more detailed information.
description: Organize content with a flexible card. description: Organize content with a flexible card.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import Icon from '@ui/Icon.astro'; import Icon from '@ui/Icon.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Carousel title: Carousel
summary: A carousel is used to display multiple pieces of visual content without taking up too much space. It eliminates the need to scroll down the page to see all content and is a popular method of presenting marketing information. summary: A carousel is used to display multiple pieces of visual content without taking up too much space. It eliminates the need to scroll down the page to see all content and is a popular method of presenting marketing information.
description: Display visual content with a carousel. description: Display visual content with a carousel.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -3,7 +3,7 @@ title: Chart
docs-libs: [apexcharts] docs-libs: [apexcharts]
summary: Tabler uses ApexCharts - a free and open-source modern charting library that helps developers to create beautiful and interactive visualizations for web pages. summary: Tabler uses ApexCharts - a free and open-source modern charting library that helps developers to create beautiful and interactive visualizations for web pages.
description: Interactive data visualizations with ApexCharts. description: Interactive data visualizations with ApexCharts.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro' import Example from '@components/Example.astro'
+1 -1
View File
@@ -3,7 +3,7 @@ title: Countup
summary: A countup element is used to display numerical data in an interesting way and make the interface more interactive. summary: A countup element is used to display numerical data in an interesting way and make the interface more interactive.
docs-libs: countup docs-libs: countup
description: Display numbers dynamically with a countup. description: Display numbers dynamically with a countup.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Data grid title: Data grid
summary: Use the data grid component to display detailed information about your product. The data is displayed as a column of items consisting of a title and content. summary: Use the data grid component to display detailed information about your product. The data is displayed as a column of items consisting of a title and content.
description: Detailed product information in grids. description: Detailed product information in grids.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Divider title: Divider
summary: Dividers help organize content and make the interface layout clear and uncluttered. Greater clarity adds up to better user experience and enhanced interaction with a website or app. summary: Dividers help organize content and make the interface layout clear and uncluttered. Greater clarity adds up to better user experience and enhanced interaction with a website or app.
description: Separate content with clear dividers. description: Separate content with clear dividers.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Dropdown title: Dropdown
summary: A dropdown is used to display a list of options or include more items in a menu without overwhelming users with too many buttons and long lists. It improves interaction with your website or software and keeps the interface clear. summary: A dropdown is used to display a list of options or include more items in a menu without overwhelming users with too many buttons and long lists. It improves interaction with your website or software and keeps the interface clear.
description: Organize options with a dropdown menu. description: Organize options with a dropdown menu.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -3,7 +3,7 @@ title: Dropzone
summary: Dropzone is a simple JavaScript library that helps you add file drag and drop functionality to your web forms. It is one of the most popular drag and drop libraries on the web and is used by millions of people. summary: Dropzone is a simple JavaScript library that helps you add file drag and drop functionality to your web forms. It is one of the most popular drag and drop libraries on the web and is used by millions of people.
description: Drag-and-drop file upload tool. description: Drag-and-drop file upload tool.
docs-libs: dropzone docs-libs: dropzone
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Empty State title: Empty State
summary: An empty state or blank page is commonly used as a placeholder for first-use, empty data, or an error screen. Its goal is to engage users when there is no content to display, which makes its design important for the overall user experience of your website or app. summary: An empty state or blank page is commonly used as a placeholder for first-use, empty data, or an error screen. Its goal is to engage users when there is no content to display, which makes its design important for the overall user experience of your website or app.
description: Engage users in an empty or error screen. description: Engage users in an empty or error screen.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import Illustration from '@ui/Illustration.astro'; import Illustration from '@ui/Illustration.astro';
+1 -1
View File
@@ -3,7 +3,7 @@ title: Icon
summary: Use an icon from over 5000 options created specifically for Tabler to make your dashboard more attractive. Each icon is available under the MIT license, so it can be used in both private and commercial projects. summary: Use an icon from over 5000 options created specifically for Tabler to make your dashboard more attractive. Each icon is available under the MIT license, so it can be used in both private and commercial projects.
banner: icons banner: icons
description: Enhance a dashboard with a custom icon. description: Enhance a dashboard with a custom icon.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import Icon from '@ui/Icon.astro'; import Icon from '@ui/Icon.astro';
+1 -1
View File
@@ -3,5 +3,5 @@ title: Components
order: 4 order: 4
description: Various components to enhance UI. description: Various components to enhance UI.
summary: Tabler UI includes a variety of components to help you build web applications that are both functional and visually appealing. From buttons and cards to modals and navigation, these components provide a wide range of features to enhance your site. summary: Tabler UI includes a variety of components to help you build web applications that are both functional and visually appealing. From buttons and cards to modals and navigation, these components provide a wide range of features to enhance your site.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
+1 -1
View File
@@ -3,7 +3,7 @@ title: Inline player
docs-libs: plyr docs-libs: plyr
summary: A simple, lightweight, accessible and customizable HTML5, YouTube and Vimeo media player that supports modern browsers. summary: A simple, lightweight, accessible and customizable HTML5, YouTube and Vimeo media player that supports modern browsers.
description: Lightweight media player for websites. description: Lightweight media player for websites.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import { Code } from 'astro:components'; import { Code } from 'astro:components';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Modal title: Modal
summary: Use Bootstrap’s JavaScript modal plugin to add dialogs to your site for lightboxes, user notifications, or completely custom content. summary: Use Bootstrap’s JavaScript modal plugin to add dialogs to your site for lightboxes, user notifications, or completely custom content.
description: Dialogs for notifications and content. description: Dialogs for notifications and content.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import Icon from '@ui/Icon.astro'; import Icon from '@ui/Icon.astro';
+1 -1
View File
@@ -1,6 +1,6 @@
--- ---
title: Offcanvas title: Offcanvas
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -1,7 +1,7 @@
--- ---
title: Pagination title: Pagination
summary: Pagination is a user interface element that allows users to navigate through a set of data or content that is divided into multiple pages. It is commonly used in web applications, blogs, and e-commerce sites to display large amounts of information in a manageable way. summary: Pagination is a user interface element that allows users to navigate through a set of data or content that is divided into multiple pages. It is commonly used in web applications, blogs, and e-commerce sites to display large amounts of information in a manageable way.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import Pagination from '@ui/Pagination.astro'; import Pagination from '@ui/Pagination.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Placeholder title: Placeholder
summary: Placeholder is used to reserve space for content that will soon appear in a layout. summary: Placeholder is used to reserve space for content that will soon appear in a layout.
description: Reserve space for upcoming content. description: Reserve space for upcoming content.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Popover title: Popover
summary: A popover is used to provide additional information for elements where a simple tooltip is not sufficient. summary: A popover is used to provide additional information for elements where a simple tooltip is not sufficient.
description: Provide extra information with a popover. description: Provide extra information with a popover.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -3,7 +3,7 @@ title: Progress Steps
summary: A progress step helps users track their place in a short process by breaking it into clear, simple steps. This makes flows like setup or onboarding easier to follow and finish. summary: A progress step helps users track their place in a short process by breaking it into clear, simple steps. This makes flows like setup or onboarding easier to follow and finish.
new: true new: true
description: Use progress steps to display compact onboarding, checkout, and setup progress. description: Use progress steps to display compact onboarding, checkout, and setup progress.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import ProgressSteps from '@ui/ProgressSteps.astro'; import ProgressSteps from '@ui/ProgressSteps.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Progress Bar title: Progress Bar
summary: A progress bar is used to provide feedback on an action status and inform users about current progress. Although it is a small interface element, it is extremely helpful in managing user expectations and preventing abandonment of an initiated process. summary: A progress bar is used to provide feedback on an action status and inform users about current progress. Although it is a small interface element, it is extremely helpful in managing user expectations and preventing abandonment of an initiated process.
description: Track and display progress with a progress bar. description: Track and display progress with a progress bar.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import ProgressBg from '@ui/ProgressBg.astro'; import ProgressBg from '@ui/ProgressBg.astro';
+1 -1
View File
@@ -3,7 +3,7 @@ title: Range slider
docs-libs: nouislider docs-libs: nouislider
description: Adjust values with a range slider. description: Adjust values with a range slider.
summary: A range slider allows users to select a range of values by adjusting two handles along a track, providing an intuitive and space-efficient input method. summary: A range slider allows users to select a range of values by adjusting two handles along a track, providing an intuitive and space-efficient input method.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
+1 -1
View File
@@ -2,7 +2,7 @@
title: Ribbon title: Ribbon
summary: A ribbon is a graphical element that attracts user attention to a given interface element and makes it stand out. summary: A ribbon is a graphical element that attracts user attention to a given interface element and makes it stand out.
description: Highlight an element with a graphical ribbon. description: Highlight an element with a graphical ribbon.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import Icon from '@ui/Icon.astro'; import Icon from '@ui/Icon.astro';
@@ -1,7 +1,7 @@
--- ---
title: Segmented Control title: Segmented Control
summary: A segmented control is a set of two or more segments, each of which functions as a mutually exclusive button. A segmented control is used to display a set of mutually exclusive options. summary: A segmented control is a set of two or more segments, each of which functions as a mutually exclusive button. A segmented control is used to display a set of mutually exclusive options.
layout: '@shared/layouts/DocsMdxLayout.astro' layout: '@layouts/DocsMdxLayout.astro'
--- ---
import Example from '@components/Example.astro'; import Example from '@components/Example.astro';
import Icon from '@ui/Icon.astro'; import Icon from '@ui/Icon.astro';
Loaded 100 of 552 files, more files were not shown because too many files have changed in this diff. Show more