@jjlmoya/utils-converters 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +61 -0
- package/src/category/i18n/en.ts +90 -0
- package/src/category/i18n/es.ts +90 -0
- package/src/category/i18n/fr.ts +90 -0
- package/src/category/index.ts +39 -0
- package/src/category/seo.astro +15 -0
- package/src/components/PreviewNavSidebar.astro +116 -0
- package/src/components/PreviewToolbar.astro +152 -0
- package/src/data.ts +34 -0
- package/src/env.d.ts +5 -0
- package/src/index.ts +39 -0
- package/src/layouts/PreviewLayout.astro +117 -0
- package/src/pages/[locale]/[slug].astro +148 -0
- package/src/pages/[locale].astro +271 -0
- package/src/pages/index.astro +4 -0
- package/src/shared/ImageConverter.astro +237 -0
- package/src/shared/logic/converter.ts +167 -0
- package/src/shared/style.css +258 -0
- package/src/tests/faq_count.test.ts +10 -0
- package/src/tests/mocks/astro_mock.js +2 -0
- package/src/tests/seo_length.test.ts +22 -0
- package/src/tests/tool_validation.test.ts +17 -0
- package/src/tool/avifAJpg/bibliography.astro +14 -0
- package/src/tool/avifAJpg/component.astro +8 -0
- package/src/tool/avifAJpg/i18n/en.ts +123 -0
- package/src/tool/avifAJpg/i18n/es.ts +123 -0
- package/src/tool/avifAJpg/i18n/fr.ts +118 -0
- package/src/tool/avifAJpg/index.ts +29 -0
- package/src/tool/avifAJpg/seo.astro +14 -0
- package/src/tool/avifAPng/bibliography.astro +14 -0
- package/src/tool/avifAPng/component.astro +8 -0
- package/src/tool/avifAPng/i18n/en.ts +118 -0
- package/src/tool/avifAPng/i18n/es.ts +123 -0
- package/src/tool/avifAPng/i18n/fr.ts +118 -0
- package/src/tool/avifAPng/index.ts +29 -0
- package/src/tool/avifAPng/seo.astro +14 -0
- package/src/tool/avifAWebp/bibliography.astro +14 -0
- package/src/tool/avifAWebp/component.astro +8 -0
- package/src/tool/avifAWebp/i18n/en.ts +118 -0
- package/src/tool/avifAWebp/i18n/es.ts +123 -0
- package/src/tool/avifAWebp/i18n/fr.ts +118 -0
- package/src/tool/avifAWebp/index.ts +29 -0
- package/src/tool/avifAWebp/seo.astro +14 -0
- package/src/tool/bmpAJpg/bibliography.astro +14 -0
- package/src/tool/bmpAJpg/component.astro +8 -0
- package/src/tool/bmpAJpg/i18n/en.ts +123 -0
- package/src/tool/bmpAJpg/i18n/es.ts +123 -0
- package/src/tool/bmpAJpg/i18n/fr.ts +118 -0
- package/src/tool/bmpAJpg/index.ts +29 -0
- package/src/tool/bmpAJpg/seo.astro +14 -0
- package/src/tool/bmpAPng/bibliography.astro +14 -0
- package/src/tool/bmpAPng/component.astro +8 -0
- package/src/tool/bmpAPng/i18n/en.ts +123 -0
- package/src/tool/bmpAPng/i18n/es.ts +123 -0
- package/src/tool/bmpAPng/i18n/fr.ts +118 -0
- package/src/tool/bmpAPng/index.ts +29 -0
- package/src/tool/bmpAPng/seo.astro +14 -0
- package/src/tool/bmpAWebp/bibliography.astro +14 -0
- package/src/tool/bmpAWebp/component.astro +8 -0
- package/src/tool/bmpAWebp/i18n/en.ts +118 -0
- package/src/tool/bmpAWebp/i18n/es.ts +123 -0
- package/src/tool/bmpAWebp/i18n/fr.ts +118 -0
- package/src/tool/bmpAWebp/index.ts +29 -0
- package/src/tool/bmpAWebp/seo.astro +14 -0
- package/src/tool/gifAJpg/bibliography.astro +14 -0
- package/src/tool/gifAJpg/component.astro +8 -0
- package/src/tool/gifAJpg/i18n/en.ts +123 -0
- package/src/tool/gifAJpg/i18n/es.ts +123 -0
- package/src/tool/gifAJpg/i18n/fr.ts +118 -0
- package/src/tool/gifAJpg/index.ts +29 -0
- package/src/tool/gifAJpg/seo.astro +14 -0
- package/src/tool/gifAPng/bibliography.astro +14 -0
- package/src/tool/gifAPng/component.astro +8 -0
- package/src/tool/gifAPng/i18n/en.ts +123 -0
- package/src/tool/gifAPng/i18n/es.ts +123 -0
- package/src/tool/gifAPng/i18n/fr.ts +118 -0
- package/src/tool/gifAPng/index.ts +29 -0
- package/src/tool/gifAPng/seo.astro +14 -0
- package/src/tool/gifAWebp/bibliography.astro +14 -0
- package/src/tool/gifAWebp/component.astro +8 -0
- package/src/tool/gifAWebp/i18n/en.ts +123 -0
- package/src/tool/gifAWebp/i18n/es.ts +123 -0
- package/src/tool/gifAWebp/i18n/fr.ts +118 -0
- package/src/tool/gifAWebp/index.ts +29 -0
- package/src/tool/gifAWebp/seo.astro +14 -0
- package/src/tool/imagenBase64/bibliography.astro +14 -0
- package/src/tool/imagenBase64/component.astro +159 -0
- package/src/tool/imagenBase64/i18n/en.ts +137 -0
- package/src/tool/imagenBase64/i18n/es.ts +137 -0
- package/src/tool/imagenBase64/i18n/fr.ts +132 -0
- package/src/tool/imagenBase64/index.ts +43 -0
- package/src/tool/imagenBase64/seo.astro +14 -0
- package/src/tool/imagenBase64/style.css +299 -0
- package/src/tool/jpgAIco/bibliography.astro +14 -0
- package/src/tool/jpgAIco/component.astro +8 -0
- package/src/tool/jpgAIco/i18n/en.ts +123 -0
- package/src/tool/jpgAIco/i18n/es.ts +123 -0
- package/src/tool/jpgAIco/i18n/fr.ts +118 -0
- package/src/tool/jpgAIco/index.ts +29 -0
- package/src/tool/jpgAIco/seo.astro +14 -0
- package/src/tool/jpgAPng/bibliography.astro +14 -0
- package/src/tool/jpgAPng/component.astro +8 -0
- package/src/tool/jpgAPng/i18n/en.ts +128 -0
- package/src/tool/jpgAPng/i18n/es.ts +128 -0
- package/src/tool/jpgAPng/i18n/fr.ts +123 -0
- package/src/tool/jpgAPng/index.ts +29 -0
- package/src/tool/jpgAPng/seo.astro +14 -0
- package/src/tool/jpgAWebp/bibliography.astro +14 -0
- package/src/tool/jpgAWebp/component.astro +8 -0
- package/src/tool/jpgAWebp/i18n/en.ts +118 -0
- package/src/tool/jpgAWebp/i18n/es.ts +123 -0
- package/src/tool/jpgAWebp/i18n/fr.ts +118 -0
- package/src/tool/jpgAWebp/index.ts +29 -0
- package/src/tool/jpgAWebp/seo.astro +14 -0
- package/src/tool/pngAIco/bibliography.astro +14 -0
- package/src/tool/pngAIco/component.astro +8 -0
- package/src/tool/pngAIco/i18n/en.ts +123 -0
- package/src/tool/pngAIco/i18n/es.ts +123 -0
- package/src/tool/pngAIco/i18n/fr.ts +118 -0
- package/src/tool/pngAIco/index.ts +29 -0
- package/src/tool/pngAIco/seo.astro +14 -0
- package/src/tool/pngAJpg/bibliography.astro +14 -0
- package/src/tool/pngAJpg/component.astro +8 -0
- package/src/tool/pngAJpg/i18n/en.ts +133 -0
- package/src/tool/pngAJpg/i18n/es.ts +201 -0
- package/src/tool/pngAJpg/i18n/fr.ts +128 -0
- package/src/tool/pngAJpg/index.ts +29 -0
- package/src/tool/pngAJpg/seo.astro +14 -0
- package/src/tool/pngAWebp/bibliography.astro +14 -0
- package/src/tool/pngAWebp/component.astro +8 -0
- package/src/tool/pngAWebp/i18n/en.ts +127 -0
- package/src/tool/pngAWebp/i18n/es.ts +132 -0
- package/src/tool/pngAWebp/i18n/fr.ts +122 -0
- package/src/tool/pngAWebp/index.ts +29 -0
- package/src/tool/pngAWebp/seo.astro +14 -0
- package/src/tool/svgAJpg/bibliography.astro +14 -0
- package/src/tool/svgAJpg/component.astro +8 -0
- package/src/tool/svgAJpg/i18n/en.ts +118 -0
- package/src/tool/svgAJpg/i18n/es.ts +123 -0
- package/src/tool/svgAJpg/i18n/fr.ts +118 -0
- package/src/tool/svgAJpg/index.ts +29 -0
- package/src/tool/svgAJpg/seo.astro +14 -0
- package/src/tool/svgAPng/bibliography.astro +14 -0
- package/src/tool/svgAPng/component.astro +8 -0
- package/src/tool/svgAPng/i18n/en.ts +123 -0
- package/src/tool/svgAPng/i18n/es.ts +128 -0
- package/src/tool/svgAPng/i18n/fr.ts +118 -0
- package/src/tool/svgAPng/index.ts +29 -0
- package/src/tool/svgAPng/seo.astro +14 -0
- package/src/tool/webpAIco/bibliography.astro +14 -0
- package/src/tool/webpAIco/component.astro +8 -0
- package/src/tool/webpAIco/i18n/en.ts +123 -0
- package/src/tool/webpAIco/i18n/es.ts +123 -0
- package/src/tool/webpAIco/i18n/fr.ts +118 -0
- package/src/tool/webpAIco/index.ts +29 -0
- package/src/tool/webpAIco/seo.astro +14 -0
- package/src/tool/webpAJpg/bibliography.astro +14 -0
- package/src/tool/webpAJpg/component.astro +8 -0
- package/src/tool/webpAJpg/i18n/en.ts +122 -0
- package/src/tool/webpAJpg/i18n/es.ts +127 -0
- package/src/tool/webpAJpg/i18n/fr.ts +122 -0
- package/src/tool/webpAJpg/index.ts +29 -0
- package/src/tool/webpAJpg/seo.astro +14 -0
- package/src/tool/webpAPng/bibliography.astro +14 -0
- package/src/tool/webpAPng/component.astro +8 -0
- package/src/tool/webpAPng/i18n/en.ts +127 -0
- package/src/tool/webpAPng/i18n/es.ts +132 -0
- package/src/tool/webpAPng/i18n/fr.ts +122 -0
- package/src/tool/webpAPng/index.ts +29 -0
- package/src/tool/webpAPng/seo.astro +14 -0
- package/src/tools.ts +70 -0
- package/src/types.ts +69 -0
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jjlmoya/utils-converters",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"types": "./src/index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts",
|
|
9
|
+
"./data": "./src/data.ts"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src"
|
|
13
|
+
],
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"dev": "astro dev",
|
|
19
|
+
"start": "astro dev",
|
|
20
|
+
"build": "astro build",
|
|
21
|
+
"preview": "astro preview",
|
|
22
|
+
"astro": "astro",
|
|
23
|
+
"lint": "eslint src/ --max-warnings 0 && stylelint \"src/**/*.{css,astro}\"",
|
|
24
|
+
"check": "astro check",
|
|
25
|
+
"type-check": "astro check",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"preversion": "npm run lint && npm run test",
|
|
28
|
+
"postversion": "git push && git push --tags",
|
|
29
|
+
"patch": "npm version patch",
|
|
30
|
+
"minor": "npm version minor",
|
|
31
|
+
"major": "npm version major"
|
|
32
|
+
},
|
|
33
|
+
"lint-staged": {
|
|
34
|
+
"*.{ts,tsx,astro}": [
|
|
35
|
+
"eslint --fix"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@iconify-json/mdi": "^1.2.3",
|
|
40
|
+
"@jjlmoya/utils-shared": "1.0.2",
|
|
41
|
+
"astro": "^6.1.2",
|
|
42
|
+
"astro-icon": "^1.1.0",
|
|
43
|
+
"jszip": "^3.10.1"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@astrojs/check": "^0.9.8",
|
|
47
|
+
"eslint": "^9.39.4",
|
|
48
|
+
"eslint-plugin-astro": "^1.6.0",
|
|
49
|
+
"eslint-plugin-no-comments": "^1.1.10",
|
|
50
|
+
"husky": "^9.1.7",
|
|
51
|
+
"lint-staged": "^16.4.0",
|
|
52
|
+
"postcss-html": "^1.8.1",
|
|
53
|
+
"schema-dts": "^1.1.2",
|
|
54
|
+
"stylelint": "^17.6.0",
|
|
55
|
+
"stylelint-config-standard": "^40.0.0",
|
|
56
|
+
"stylelint-declaration-strict-value": "^1.11.1",
|
|
57
|
+
"typescript": "^5.4.0",
|
|
58
|
+
"typescript-eslint": "^8.58.0",
|
|
59
|
+
"vitest": "^4.1.2"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'image-converters',
|
|
5
|
+
title: 'Online Image Converters',
|
|
6
|
+
description: 'Free tools to convert images between PNG, JPG, WebP, SVG, AVIF, GIF, BMP and ICO formats. Everything is processed locally in your browser without uploading anything to the internet.',
|
|
7
|
+
seo: [
|
|
8
|
+
{
|
|
9
|
+
type: 'summary',
|
|
10
|
+
title: 'Why use our converters',
|
|
11
|
+
items: [
|
|
12
|
+
'100% local conversion: your images never leave your device.',
|
|
13
|
+
'Support for 21 format combinations: PNG, JPG, WebP, SVG, AVIF, GIF, BMP and ICO.',
|
|
14
|
+
'Batch processing with ZIP download.',
|
|
15
|
+
'No registration, no limits, no intrusive ads.',
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
type: 'title',
|
|
20
|
+
text: 'Image Conversion: Complete Format Guide',
|
|
21
|
+
level: 2,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
type: 'paragraph',
|
|
25
|
+
html: 'Choosing the right image format has a direct impact on web performance, device compatibility and perceived visual quality. Our <strong>online image converters</strong> cover the most demanded conversion routes by designers, web developers and photographers, all without relying on external servers that could compromise the privacy of your files.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: 'title',
|
|
29
|
+
text: 'PNG, JPG and WebP: The Three Pillars of Modern Web Design',
|
|
30
|
+
level: 2,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: 'paragraph',
|
|
34
|
+
html: '<strong>PNG format</strong> stands out for its lossless compression and transparency support (alpha channel), being the standard for logos, icons and graphics with sharp edges. <strong>JPG format</strong> offers maximum universal compatibility and reduced sizes for photos, but sacrifices quality with each save. <strong>WebP format</strong>, developed by Google, combines the best of both: 30-40% better compression than PNG/JPG with transparency and animation support, now the recommended format by Core Web Vitals to improve LCP.',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: 'table',
|
|
38
|
+
headers: ['Format', 'Transparency', 'Compression', 'Best for'],
|
|
39
|
+
rows: [
|
|
40
|
+
['PNG', 'Yes', 'Lossless', 'Logos, graphics, screenshots'],
|
|
41
|
+
['JPG', 'No', 'Lossy', 'Photos, large images'],
|
|
42
|
+
['WebP', 'Yes', 'Lossless/Lossy', 'Modern web, performance'],
|
|
43
|
+
['SVG', 'Yes', 'Vector', 'Scalable icons, animations'],
|
|
44
|
+
['AVIF', 'Yes', 'Superior', 'Next-gen web'],
|
|
45
|
+
['ICO', 'Yes', 'Bitmap', 'Favicons, Windows icons'],
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: 'title',
|
|
50
|
+
text: 'Next-Generation Formats: AVIF and WebP',
|
|
51
|
+
level: 2,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: 'paragraph',
|
|
55
|
+
html: '<strong>AVIF format</strong> (AV1 Image File Format) is the technological successor of WebP, offering up to 50% less file size than JPG at equal subjective quality. Although its adoption grows rapidly, limited compatibility in older systems makes converting AVIF to more established formats like JPG, PNG or WebP a frequent need in production workflows.',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
type: 'tip',
|
|
59
|
+
title: 'Web performance tip',
|
|
60
|
+
html: 'To maximize page load speed, use WebP as the primary format on your website with JPG/PNG fallbacks for older browsers. The HTML <code>picture</code> element with multiple <code>source</code> tags lets you serve the optimal format based on the visiting browser\'s support.',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: 'title',
|
|
64
|
+
text: 'SVG, BMP and ICO: Special Use Cases',
|
|
65
|
+
level: 2,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: 'paragraph',
|
|
69
|
+
html: '<strong>SVG files</strong> are mathematical vectors that scale losslessly to any resolution, perfect for logos and graphic design. However, many platforms and editing applications do not support them directly, making it necessary to rasterize them to PNG or JPG for distribution. <strong>BMP files</strong>, though obsolete for web use, remain common in Windows environments and legacy editing software. Finally, the <strong>ICO format</strong> is the standard for web favicons and Windows icon customization, requiring a specific binary header structure that our converter generates natively.',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
type: 'stats',
|
|
73
|
+
columns: 3,
|
|
74
|
+
items: [
|
|
75
|
+
{ label: 'Combinations', value: '21', icon: 'mdi:image-sync' },
|
|
76
|
+
{ label: 'Privacy', value: '100% local', icon: 'mdi:shield-lock' },
|
|
77
|
+
{ label: 'Formats', value: '8 types', icon: 'mdi:file-image' },
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: 'title',
|
|
82
|
+
text: 'Total Privacy: Conversion without Servers',
|
|
83
|
+
level: 2,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
type: 'paragraph',
|
|
87
|
+
html: 'Unlike other online tools that upload your images to remote servers (with the privacy and security risks that entails), our converters use exclusively native browser APIs: <strong>Canvas 2D API</strong> for rasterization, <strong>FileReader API</strong> to read files locally and <strong>Blob/URL.createObjectURL</strong> for downloads. This means you can convert confidential images, corporate logos or private documents with complete security.',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'convertidores-imagen',
|
|
5
|
+
title: 'Convertidores de Imagen Online',
|
|
6
|
+
description: 'Herramientas gratuitas para convertir imágenes entre formatos PNG, JPG, WebP, SVG, AVIF, GIF, BMP e ICO. Todo se procesa localmente en tu navegador sin subir nada a internet.',
|
|
7
|
+
seo: [
|
|
8
|
+
{
|
|
9
|
+
type: 'summary',
|
|
10
|
+
title: 'Por qué usar nuestros convertidores',
|
|
11
|
+
items: [
|
|
12
|
+
'Conversión 100% local: tus imágenes nunca salen de tu dispositivo.',
|
|
13
|
+
'Soporte para 21 combinaciones de formatos: PNG, JPG, WebP, SVG, AVIF, GIF, BMP e ICO.',
|
|
14
|
+
'Procesamiento por lotes con descarga en ZIP.',
|
|
15
|
+
'Sin registro, sin límites, sin publicidad intrusiva.',
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
type: 'title',
|
|
20
|
+
text: 'Conversión de Imágenes: Guía Completa de Formatos',
|
|
21
|
+
level: 2,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
type: 'paragraph',
|
|
25
|
+
html: 'La elección del formato de imagen adecuado tiene un impacto directo en el rendimiento web, la compatibilidad con dispositivos y la calidad visual percibida. Nuestros <strong>convertidores de imagen online</strong> cubren las rutas de conversión más demandadas por diseñadores, desarrolladores web y fotógrafos, todo ello sin depender de servidores externos que podrían comprometer la privacidad de tus archivos.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: 'title',
|
|
29
|
+
text: 'PNG, JPG y WebP: Los Tres Pilares del Diseño Web Moderno',
|
|
30
|
+
level: 2,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: 'paragraph',
|
|
34
|
+
html: 'El <strong>formato PNG</strong> destaca por su compresión sin pérdida y soporte para transparencias (canal alfa), siendo el estándar para logotipos, iconos y gráficos con bordes nítidos. El <strong>formato JPG</strong> ofrece la máxima compatibilidad universal y tamaños reducidos para fotografías, pero sacrifica calidad en cada guardado. El <strong>formato WebP</strong>, desarrollado por Google, combina lo mejor de ambos: compresión un 30-40% superior a PNG/JPG con soporte de transparencias y animaciones, siendo hoy el formato recomendado por Core Web Vitals para mejorar el LCP.',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: 'table',
|
|
38
|
+
headers: ['Formato', 'Transparencia', 'Compresión', 'Mejor para'],
|
|
39
|
+
rows: [
|
|
40
|
+
['PNG', 'Sí', 'Sin pérdida', 'Logos, gráficos, capturas'],
|
|
41
|
+
['JPG', 'No', 'Con pérdida', 'Fotografías, imágenes grandes'],
|
|
42
|
+
['WebP', 'Sí', 'Sin/Con pérdida', 'Web moderna, rendimiento'],
|
|
43
|
+
['SVG', 'Sí', 'Vectorial', 'Iconos escalables, animaciones'],
|
|
44
|
+
['AVIF', 'Sí', 'Superior', 'Siguiente generación web'],
|
|
45
|
+
['ICO', 'Sí', 'Bitmap', 'Favicons, iconos Windows'],
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: 'title',
|
|
50
|
+
text: 'Formatos de Nueva Generación: AVIF y WebP',
|
|
51
|
+
level: 2,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: 'paragraph',
|
|
55
|
+
html: 'El <strong>formato AVIF</strong> (AV1 Image File Format) es el sucesor tecnológico del WebP, ofreciendo hasta un 50% menos de peso que el JPG a igual calidad subjetiva. Aunque su adopción crece rápidamente, la compatibilidad limitada en sistemas antiguos hace que convertir AVIF a formatos más establecidos como JPG, PNG o WebP sea una necesidad frecuente en flujos de trabajo de producción.',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
type: 'tip',
|
|
59
|
+
title: 'Consejo de rendimiento web',
|
|
60
|
+
html: 'Para maximizar la velocidad de carga, usa WebP como formato principal en tu web con fallbacks JPG/PNG para navegadores antiguos. Herramientas como <code>picture</code> con múltiples <code>source</code> te permiten servir el formato óptimo según el soporte del navegador visitante.',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: 'title',
|
|
64
|
+
text: 'SVG, BMP e ICO: Casos de Uso Especiales',
|
|
65
|
+
level: 2,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: 'paragraph',
|
|
69
|
+
html: 'Los <strong>archivos SVG</strong> son vectores matemáticos que escalan sin pérdida a cualquier resolución, perfectos para logotipos y diseño gráfico. Sin embargo, muchas plataformas y aplicaciones de edición no los soportan directamente, lo que hace necesario rasterizarlos a PNG o JPG para distribución. Los <strong>archivos BMP</strong>, aunque obsoletos para uso web, siguen siendo comunes en entornos Windows y software de edición legacy. Finalmente, el <strong>formato ICO</strong> es el estándar para favicons web y personalización de iconos en Windows, requiriendo una estructura de cabecera binaria específica que nuestro convertidor genera de forma nativa.',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
type: 'stats',
|
|
73
|
+
columns: 3,
|
|
74
|
+
items: [
|
|
75
|
+
{ label: 'Combinaciones', value: '21', icon: 'mdi:image-sync' },
|
|
76
|
+
{ label: 'Privacidad', value: '100% local', icon: 'mdi:shield-lock' },
|
|
77
|
+
{ label: 'Formatos', value: '8 tipos', icon: 'mdi:file-image' },
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: 'title',
|
|
82
|
+
text: 'Privacidad Total: Conversión sin Servidores',
|
|
83
|
+
level: 2,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
type: 'paragraph',
|
|
87
|
+
html: 'A diferencia de otras herramientas online que suben tus imágenes a servidores remotos (con los riesgos de privacidad y seguridad que eso implica), nuestros convertidores utilizan exclusivamente las APIs nativas del navegador: <strong>Canvas 2D API</strong> para la rasterización, <strong>FileReader API</strong> para leer los archivos localmente y <strong>Blob/URL.createObjectURL</strong> para las descargas. Esto significa que puedes convertir imágenes confidenciales, logos corporativos o documentos privados con total seguridad.',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { CategoryLocaleContent } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const content: CategoryLocaleContent = {
|
|
4
|
+
slug: 'convertisseurs-image',
|
|
5
|
+
title: 'Convertisseurs d\'Image en Ligne',
|
|
6
|
+
description: 'Outils gratuits pour convertir des images entre les formats PNG, JPG, WebP, SVG, AVIF, GIF, BMP et ICO. Tout est traité localement dans votre navigateur sans rien télécharger sur internet.',
|
|
7
|
+
seo: [
|
|
8
|
+
{
|
|
9
|
+
type: 'summary',
|
|
10
|
+
title: 'Pourquoi utiliser nos convertisseurs',
|
|
11
|
+
items: [
|
|
12
|
+
'Conversion 100% locale : vos images ne quittent jamais votre appareil.',
|
|
13
|
+
'Support de 21 combinaisons de formats : PNG, JPG, WebP, SVG, AVIF, GIF, BMP et ICO.',
|
|
14
|
+
'Traitement par lots avec téléchargement ZIP.',
|
|
15
|
+
'Sans inscription, sans limites, sans publicité intrusive.',
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
type: 'title',
|
|
20
|
+
text: 'Conversion d\'Images : Guide Complet des Formats',
|
|
21
|
+
level: 2,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
type: 'paragraph',
|
|
25
|
+
html: 'Le choix du format d\'image approprié a un impact direct sur les performances web, la compatibilité avec les appareils et la qualité visuelle perçue. Nos <strong>convertisseurs d\'image en ligne</strong> couvrent les routes de conversion les plus demandées par les designers, développeurs web et photographes, sans dépendre de serveurs externes qui pourraient compromettre la confidentialité de vos fichiers.',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
type: 'title',
|
|
29
|
+
text: 'PNG, JPG et WebP : Les Trois Piliers du Web Moderne',
|
|
30
|
+
level: 2,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: 'paragraph',
|
|
34
|
+
html: 'Le <strong>format PNG</strong> se distingue par sa compression sans perte et son support de la transparence (canal alpha), étant le standard pour les logos, icônes et graphiques aux bords nets. Le <strong>format JPG</strong> offre une compatibilité universelle maximale et des tailles réduites pour les photos, mais sacrifie la qualité à chaque sauvegarde. Le <strong>format WebP</strong>, développé par Google, combine le meilleur des deux : une compression 30-40% supérieure à PNG/JPG avec support de la transparence et des animations.',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: 'table',
|
|
38
|
+
headers: ['Format', 'Transparence', 'Compression', 'Idéal pour'],
|
|
39
|
+
rows: [
|
|
40
|
+
['PNG', 'Oui', 'Sans perte', 'Logos, graphiques, captures'],
|
|
41
|
+
['JPG', 'Non', 'Avec perte', 'Photos, grandes images'],
|
|
42
|
+
['WebP', 'Oui', 'Sans/Avec perte', 'Web moderne, performance'],
|
|
43
|
+
['SVG', 'Oui', 'Vectoriel', 'Icônes scalables, animations'],
|
|
44
|
+
['AVIF', 'Oui', 'Supérieure', 'Web nouvelle génération'],
|
|
45
|
+
['ICO', 'Oui', 'Bitmap', 'Favicons, icônes Windows'],
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: 'title',
|
|
50
|
+
text: 'Formats Nouvelle Génération : AVIF et WebP',
|
|
51
|
+
level: 2,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: 'paragraph',
|
|
55
|
+
html: 'Le <strong>format AVIF</strong> (AV1 Image File Format) est le successeur technologique du WebP, offrant jusqu\'à 50% de poids en moins que le JPG à qualité subjective égale. Bien que son adoption croisse rapidement, la compatibilité limitée sur les systèmes anciens rend souvent nécessaire la conversion d\'AVIF vers des formats plus établis comme JPG, PNG ou WebP.',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
type: 'tip',
|
|
59
|
+
title: 'Conseil performance web',
|
|
60
|
+
html: 'Pour maximiser la vitesse de chargement, utilisez WebP comme format principal sur votre site avec des alternatives JPG/PNG pour les navigateurs anciens. L\'élément HTML <code>picture</code> avec plusieurs balises <code>source</code> vous permet de servir le format optimal selon le support du navigateur visiteur.',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
type: 'title',
|
|
64
|
+
text: 'SVG, BMP et ICO : Cas d\'Usage Spéciaux',
|
|
65
|
+
level: 2,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: 'paragraph',
|
|
69
|
+
html: 'Les <strong>fichiers SVG</strong> sont des vecteurs mathématiques qui s\'adaptent sans perte à n\'importe quelle résolution, parfaits pour les logos et la conception graphique. Cependant, de nombreuses plateformes ne les supportent pas directement. Les <strong>fichiers BMP</strong>, bien qu\'obsolètes pour le web, restent courants dans les environnements Windows. Enfin, le <strong>format ICO</strong> est le standard pour les favicons web et la personnalisation des icônes Windows.',
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
type: 'stats',
|
|
73
|
+
columns: 3,
|
|
74
|
+
items: [
|
|
75
|
+
{ label: 'Combinaisons', value: '21', icon: 'mdi:image-sync' },
|
|
76
|
+
{ label: 'Confidentialité', value: '100% local', icon: 'mdi:shield-lock' },
|
|
77
|
+
{ label: 'Formats', value: '8 types', icon: 'mdi:file-image' },
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: 'title',
|
|
82
|
+
text: 'Confidentialité Totale : Conversion sans Serveurs',
|
|
83
|
+
level: 2,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
type: 'paragraph',
|
|
87
|
+
html: 'Contrairement à d\'autres outils en ligne qui téléchargent vos images sur des serveurs distants, nos convertisseurs utilisent exclusivement les APIs natives du navigateur : <strong>Canvas 2D API</strong> pour la rastérisation, <strong>FileReader API</strong> pour lire les fichiers localement et <strong>Blob/URL.createObjectURL</strong> pour les téléchargements. Vous pouvez ainsi convertir des images confidentielles, des logos d\'entreprise ou des documents privés en toute sécurité.',
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ConvertersCategoryEntry } from '../types';
|
|
2
|
+
import { pngAJpg } from '../tool/pngAJpg';
|
|
3
|
+
import { jpgAPng } from '../tool/jpgAPng';
|
|
4
|
+
import { webpAPng } from '../tool/webpAPng';
|
|
5
|
+
import { webpAJpg } from '../tool/webpAJpg';
|
|
6
|
+
import { pngAWebp } from '../tool/pngAWebp';
|
|
7
|
+
import { jpgAWebp } from '../tool/jpgAWebp';
|
|
8
|
+
import { svgAPng } from '../tool/svgAPng';
|
|
9
|
+
import { svgAJpg } from '../tool/svgAJpg';
|
|
10
|
+
import { imagenBase64 } from '../tool/imagenBase64';
|
|
11
|
+
import { bmpAJpg } from '../tool/bmpAJpg';
|
|
12
|
+
import { bmpAPng } from '../tool/bmpAPng';
|
|
13
|
+
import { bmpAWebp } from '../tool/bmpAWebp';
|
|
14
|
+
import { avifAJpg } from '../tool/avifAJpg';
|
|
15
|
+
import { avifAPng } from '../tool/avifAPng';
|
|
16
|
+
import { avifAWebp } from '../tool/avifAWebp';
|
|
17
|
+
import { gifAJpg } from '../tool/gifAJpg';
|
|
18
|
+
import { gifAPng } from '../tool/gifAPng';
|
|
19
|
+
import { gifAWebp } from '../tool/gifAWebp';
|
|
20
|
+
import { pngAIco } from '../tool/pngAIco';
|
|
21
|
+
import { jpgAIco } from '../tool/jpgAIco';
|
|
22
|
+
import { webpAIco } from '../tool/webpAIco';
|
|
23
|
+
|
|
24
|
+
export const convertersCategory: ConvertersCategoryEntry = {
|
|
25
|
+
icon: 'mdi:image-sync',
|
|
26
|
+
tools: [
|
|
27
|
+
pngAJpg, jpgAPng, webpAPng, webpAJpg, pngAWebp, jpgAWebp,
|
|
28
|
+
svgAPng, svgAJpg, imagenBase64,
|
|
29
|
+
bmpAJpg, bmpAPng, bmpAWebp,
|
|
30
|
+
avifAJpg, avifAPng, avifAWebp,
|
|
31
|
+
gifAJpg, gifAPng, gifAWebp,
|
|
32
|
+
pngAIco, jpgAIco, webpAIco,
|
|
33
|
+
],
|
|
34
|
+
i18n: {
|
|
35
|
+
es: () => import('./i18n/es').then((m) => m.content),
|
|
36
|
+
en: () => import('./i18n/en').then((m) => m.content),
|
|
37
|
+
fr: () => import('./i18n/fr').then((m) => m.content),
|
|
38
|
+
},
|
|
39
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { convertersCategory } from './index';
|
|
4
|
+
import type { KnownLocale } from '../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'es' } = Astro.props;
|
|
11
|
+
const content = await convertersCategory.i18n[locale]?.();
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
{content && <SEORenderer content={{ locale, sections: content.seo }} />}
|
|
15
|
+
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
interface NavItem {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
href: string;
|
|
6
|
+
isActive?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
categoryTitle: string;
|
|
11
|
+
tools?: NavItem[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { categoryTitle, tools = [] } = Astro.props;
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<nav class="preview-nav-sidebar">
|
|
18
|
+
<div class="sidebar-header">
|
|
19
|
+
<h3>{categoryTitle}</h3>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<ul class="tools-list">
|
|
23
|
+
{tools.map((tool) => (
|
|
24
|
+
<li>
|
|
25
|
+
<a
|
|
26
|
+
href={tool.href}
|
|
27
|
+
class:list={['tool-link', { active: tool.isActive }]}
|
|
28
|
+
>
|
|
29
|
+
<span class="tool-title">{tool.title}</span>
|
|
30
|
+
</a>
|
|
31
|
+
</li>
|
|
32
|
+
))}
|
|
33
|
+
</ul>
|
|
34
|
+
</nav>
|
|
35
|
+
|
|
36
|
+
<style>
|
|
37
|
+
.preview-nav-sidebar {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
height: 100%;
|
|
41
|
+
background: var(--bg-surface, #0f172a);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.sidebar-header {
|
|
45
|
+
padding: 2rem 1.5rem 1.5rem;
|
|
46
|
+
border-bottom: 1px solid var(--border-color, #1e293b);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.sidebar-header h3 {
|
|
50
|
+
margin: 0;
|
|
51
|
+
font-size: 0.75rem;
|
|
52
|
+
font-weight: 900;
|
|
53
|
+
text-transform: uppercase;
|
|
54
|
+
letter-spacing: 0.1em;
|
|
55
|
+
color: var(--text-muted, #94a3b8);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.tools-list {
|
|
59
|
+
list-style: none;
|
|
60
|
+
margin: 0;
|
|
61
|
+
padding: 0.5rem 0;
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: column;
|
|
64
|
+
gap: 0.25rem;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.tools-list li {
|
|
68
|
+
margin: 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.tool-link {
|
|
72
|
+
display: flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
padding: 0.75rem 1.5rem;
|
|
75
|
+
color: var(--text-muted, #94a3b8);
|
|
76
|
+
text-decoration: none;
|
|
77
|
+
font-size: 0.9375rem;
|
|
78
|
+
font-weight: 500;
|
|
79
|
+
transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);
|
|
80
|
+
border-left: 3px solid transparent;
|
|
81
|
+
position: relative;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.tool-link:hover {
|
|
85
|
+
color: var(--text-base, #f1f5f9);
|
|
86
|
+
background: rgba(255, 255, 255, 0.08);
|
|
87
|
+
padding-left: 1.75rem;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.tool-link.active {
|
|
91
|
+
color: var(--accent, #f43f5e);
|
|
92
|
+
background: rgba(244, 63, 94, 0.15);
|
|
93
|
+
border-left-color: var(--accent, #f43f5e);
|
|
94
|
+
font-weight: 600;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.tool-link.active::before {
|
|
98
|
+
content: '';
|
|
99
|
+
position: absolute;
|
|
100
|
+
left: 0;
|
|
101
|
+
top: 50%;
|
|
102
|
+
transform: translateY(-50%);
|
|
103
|
+
width: 3px;
|
|
104
|
+
height: 24px;
|
|
105
|
+
background: var(--accent, #f43f5e);
|
|
106
|
+
border-radius: 0 2px 2px 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.tool-title {
|
|
110
|
+
display: block;
|
|
111
|
+
overflow: hidden;
|
|
112
|
+
text-overflow: ellipsis;
|
|
113
|
+
white-space: nowrap;
|
|
114
|
+
}
|
|
115
|
+
</style>
|
|
116
|
+
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { KnownLocale } from '../types';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
currentLocale: KnownLocale;
|
|
6
|
+
localeUrls?: Partial<Record<KnownLocale, string>>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { currentLocale, localeUrls = {} } = Astro.props;
|
|
10
|
+
|
|
11
|
+
const otherLocales = Object.entries(localeUrls).filter(([l]) => l !== currentLocale) as [
|
|
12
|
+
KnownLocale,
|
|
13
|
+
string,
|
|
14
|
+
][];
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<nav class="preview-toolbar">
|
|
18
|
+
{
|
|
19
|
+
otherLocales.length > 0 && (
|
|
20
|
+
<div class="locale-group">
|
|
21
|
+
<span class="locale-current">{currentLocale.toUpperCase()}</span>
|
|
22
|
+
{otherLocales.map(([locale, url]) => (
|
|
23
|
+
<a href={url} class="btn-locale">
|
|
24
|
+
{locale.toUpperCase()}
|
|
25
|
+
</a>
|
|
26
|
+
))}
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
<button id="theme-toggle" class="btn-theme" aria-label="Toggle theme" title="Toggle theme">
|
|
32
|
+
<span class="theme-icon"></span>
|
|
33
|
+
</button>
|
|
34
|
+
</nav>
|
|
35
|
+
|
|
36
|
+
<script>
|
|
37
|
+
function initToolbar() {
|
|
38
|
+
const btn = document.getElementById('theme-toggle');
|
|
39
|
+
if (!btn) return;
|
|
40
|
+
|
|
41
|
+
const applyTheme = (theme: string) => {
|
|
42
|
+
document.documentElement.classList.remove('theme-light', 'theme-dark');
|
|
43
|
+
document.documentElement.classList.add(theme);
|
|
44
|
+
localStorage.setItem('theme', theme);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
btn.addEventListener('click', () => {
|
|
48
|
+
const next = document.documentElement.classList.contains('theme-dark')
|
|
49
|
+
? 'theme-light'
|
|
50
|
+
: 'theme-dark';
|
|
51
|
+
applyTheme(next);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
initToolbar();
|
|
56
|
+
document.addEventListener('astro:after-swap', initToolbar);
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<style>
|
|
60
|
+
.preview-toolbar {
|
|
61
|
+
position: fixed;
|
|
62
|
+
top: 1rem;
|
|
63
|
+
right: 1.5rem;
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: 0.5rem;
|
|
67
|
+
z-index: 1000;
|
|
68
|
+
background: var(--bg-surface);
|
|
69
|
+
padding: 0.4rem;
|
|
70
|
+
border-radius: 1rem;
|
|
71
|
+
border: 1px solid var(--border-color);
|
|
72
|
+
backdrop-filter: blur(12px);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.locale-group {
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
gap: 0.25rem;
|
|
79
|
+
padding-right: 0.5rem;
|
|
80
|
+
border-right: 1px solid var(--border-color);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.locale-current {
|
|
84
|
+
font-size: 0.7rem;
|
|
85
|
+
font-weight: 900;
|
|
86
|
+
color: var(--text-muted);
|
|
87
|
+
padding: 0.4rem 0.6rem;
|
|
88
|
+
letter-spacing: 0.08em;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.btn-locale {
|
|
92
|
+
background: transparent;
|
|
93
|
+
border: 1px solid var(--border-color);
|
|
94
|
+
color: var(--text-main);
|
|
95
|
+
cursor: pointer;
|
|
96
|
+
padding: 0.4rem 0.7rem;
|
|
97
|
+
border-radius: 0.5rem;
|
|
98
|
+
font-weight: 700;
|
|
99
|
+
font-size: 0.7rem;
|
|
100
|
+
text-decoration: none;
|
|
101
|
+
letter-spacing: 0.08em;
|
|
102
|
+
transition: all 0.2s ease;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.btn-locale:hover {
|
|
106
|
+
border-color: var(--accent);
|
|
107
|
+
color: var(--accent);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.btn-theme {
|
|
111
|
+
background: transparent;
|
|
112
|
+
border: none;
|
|
113
|
+
cursor: pointer;
|
|
114
|
+
padding: 0.5rem;
|
|
115
|
+
border-radius: 0.6rem;
|
|
116
|
+
display: flex;
|
|
117
|
+
align-items: center;
|
|
118
|
+
justify-content: center;
|
|
119
|
+
transition: background 0.2s ease;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.btn-theme:hover {
|
|
123
|
+
background: rgba(255, 255, 255, 0.08);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.theme-icon {
|
|
127
|
+
display: block;
|
|
128
|
+
width: 16px;
|
|
129
|
+
height: 16px;
|
|
130
|
+
border-radius: 50%;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
:global(.theme-light) .theme-icon {
|
|
134
|
+
--sun-color: #f59e0b;
|
|
135
|
+
--sun-shadow-color: #fbbf24;
|
|
136
|
+
--sun-shadow-blur: 8px;
|
|
137
|
+
|
|
138
|
+
background: var(--sun-color);
|
|
139
|
+
box-shadow: 0 0 var(--sun-shadow-blur) var(--sun-shadow-color);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
:global(.theme-dark) .theme-icon {
|
|
143
|
+
--moon-color: #94a3b8;
|
|
144
|
+
--moon-shadow-color: #1e293b;
|
|
145
|
+
--moon-offset-x: -4px;
|
|
146
|
+
--moon-offset-y: -2px;
|
|
147
|
+
|
|
148
|
+
background: var(--moon-color);
|
|
149
|
+
box-shadow: inset var(--moon-offset-x) var(--moon-offset-y) 0 var(--moon-shadow-color);
|
|
150
|
+
}
|
|
151
|
+
</style>
|
|
152
|
+
|