astro-blog-kit 0.2.8 → 0.3.1

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.
@@ -170,14 +170,14 @@ const { postId, apiRoute = "/api/comments", replyToId } = Astro.props;
170
170
  max-width: 720px;
171
171
  margin: 3rem auto 0;
172
172
  padding: 0 2rem 4rem;
173
- font-family: var(--font-body);
173
+ font-family: var(--bk-font-body);
174
174
  }
175
175
 
176
176
  .comment-form__title {
177
- font-family: var(--font-display);
177
+ font-family: var(--bk-font-display);
178
178
  font-size: 1.35rem;
179
179
  font-weight: 700;
180
- color: var(--color-text);
180
+ color: var(--bk-text);
181
181
  margin: 0 0 1.5rem;
182
182
  }
183
183
 
@@ -206,9 +206,9 @@ const { postId, apiRoute = "/api/comments", replyToId } = Astro.props;
206
206
  align-items: center;
207
207
  gap: 0.5rem;
208
208
  font-size: 0.85rem;
209
- color: var(--color-muted);
210
- background: var(--color-surface);
211
- border: 1px solid var(--color-border);
209
+ color: var(--bk-muted);
210
+ background: var(--bk-surface);
211
+ border: 1px solid var(--bk-border);
212
212
  border-radius: 6px;
213
213
  padding: 0.5rem 0.75rem;
214
214
  margin-bottom: 1rem;
@@ -224,12 +224,12 @@ const { postId, apiRoute = "/api/comments", replyToId } = Astro.props;
224
224
  border: none;
225
225
  cursor: pointer;
226
226
  font-size: 0.8rem;
227
- color: var(--color-muted);
227
+ color: var(--bk-muted);
228
228
  padding: 0;
229
229
  }
230
230
 
231
231
  .comment-form__reply-indicator button:hover {
232
- color: var(--color-accent);
232
+ color: var(--bk-accent);
233
233
  }
234
234
 
235
235
  .comment-form__row {
@@ -254,25 +254,25 @@ const { postId, apiRoute = "/api/comments", replyToId } = Astro.props;
254
254
  .comment-form__label {
255
255
  font-size: 0.8rem;
256
256
  font-weight: 600;
257
- color: var(--color-text);
258
- font-family: var(--font-mono);
257
+ color: var(--bk-text);
258
+ font-family: var(--bk-font-mono);
259
259
  letter-spacing: 0.5px;
260
260
  text-transform: uppercase;
261
261
  }
262
262
 
263
263
  .comment-form__label span {
264
- color: var(--color-accent);
264
+ color: var(--bk-accent);
265
265
  }
266
266
 
267
267
  .comment-form__input,
268
268
  .comment-form__textarea {
269
- background: var(--color-surface);
270
- border: 1px solid var(--color-border);
269
+ background: var(--bk-surface);
270
+ border: 1px solid var(--bk-border);
271
271
  border-radius: 6px;
272
272
  padding: 0.65rem 0.9rem;
273
273
  font-size: 0.95rem;
274
- font-family: var(--font-body);
275
- color: var(--color-text);
274
+ font-family: var(--bk-font-body);
275
+ color: var(--bk-text);
276
276
  transition: border-color 0.15s;
277
277
  width: 100%;
278
278
  box-sizing: border-box;
@@ -281,7 +281,7 @@ const { postId, apiRoute = "/api/comments", replyToId } = Astro.props;
281
281
  .comment-form__input:focus,
282
282
  .comment-form__textarea:focus {
283
283
  outline: none;
284
- border-color: var(--color-accent);
284
+ border-color: var(--bk-accent);
285
285
  }
286
286
 
287
287
  .comment-form__textarea {
@@ -291,20 +291,20 @@ const { postId, apiRoute = "/api/comments", replyToId } = Astro.props;
291
291
 
292
292
  .comment-form__hint {
293
293
  font-size: 0.75rem;
294
- color: var(--color-muted);
294
+ color: var(--bk-muted);
295
295
  margin: 0;
296
296
  }
297
297
 
298
298
  .comment-form__submit {
299
299
  margin-top: 1.25rem;
300
300
  padding: 0.7rem 1.75rem;
301
- background: var(--color-accent);
302
- color: var(--color-bg);
301
+ background: var(--bk-accent);
302
+ color: var(--bk-bg);
303
303
  border: none;
304
304
  border-radius: 6px;
305
305
  font-size: 0.9rem;
306
306
  font-weight: 700;
307
- font-family: var(--font-mono);
307
+ font-family: var(--bk-font-mono);
308
308
  letter-spacing: 0.5px;
309
309
  cursor: pointer;
310
310
  transition: opacity 0.15s;
@@ -121,21 +121,21 @@ const getReplies = (parentId: number) =>
121
121
  max-width: 720px;
122
122
  margin: 4rem auto 0;
123
123
  padding: 0 2rem;
124
- font-family: var(--font-body);
124
+ font-family: var(--bk-font-body);
125
125
  }
126
126
 
127
127
  .comments__title {
128
- font-family: var(--font-display);
128
+ font-family: var(--bk-font-display);
129
129
  font-size: 1.5rem;
130
130
  font-weight: 700;
131
- color: var(--color-text);
131
+ color: var(--bk-text);
132
132
  margin: 0 0 2rem;
133
133
  padding-bottom: 1rem;
134
- border-bottom: 1px solid var(--color-border);
134
+ border-bottom: 1px solid var(--bk-border);
135
135
  }
136
136
 
137
137
  .comments__empty {
138
- color: var(--color-muted);
138
+ color: var(--bk-muted);
139
139
  font-size: 0.95rem;
140
140
  }
141
141
 
@@ -157,7 +157,7 @@ const getReplies = (parentId: number) =>
157
157
 
158
158
  .comment--reply {
159
159
  padding-left: 1.5rem;
160
- border-left: 2px solid var(--color-border);
160
+ border-left: 2px solid var(--bk-border);
161
161
  }
162
162
 
163
163
  .comment__header {
@@ -171,7 +171,7 @@ const getReplies = (parentId: number) =>
171
171
  height: 48px;
172
172
  border-radius: 50%;
173
173
  object-fit: cover;
174
- border: 2px solid var(--color-border);
174
+ border: 2px solid var(--bk-border);
175
175
  flex-shrink: 0;
176
176
  }
177
177
 
@@ -189,18 +189,18 @@ const getReplies = (parentId: number) =>
189
189
  .comment__author {
190
190
  font-size: 0.9rem;
191
191
  font-weight: 700;
192
- color: var(--color-text);
192
+ color: var(--bk-text);
193
193
  }
194
194
 
195
195
  .comment__date {
196
196
  font-size: 0.75rem;
197
- color: var(--color-muted);
198
- font-family: var(--font-mono);
197
+ color: var(--bk-muted);
198
+ font-family: var(--bk-font-mono);
199
199
  }
200
200
 
201
201
  .comment__body {
202
202
  font-size: 0.95rem;
203
- color: var(--color-muted-light);
203
+ color: var(--bk-muted-light);
204
204
  line-height: 1.75;
205
205
  }
206
206
 
@@ -218,14 +218,14 @@ const getReplies = (parentId: number) =>
218
218
  border: none;
219
219
  cursor: pointer;
220
220
  font-size: 0.8rem;
221
- font-family: var(--font-mono);
222
- color: var(--color-muted);
221
+ font-family: var(--bk-font-mono);
222
+ color: var(--bk-muted);
223
223
  padding: 0;
224
224
  transition: color 0.15s;
225
225
  }
226
226
 
227
227
  .comment__reply-btn:hover {
228
- color: var(--color-accent);
228
+ color: var(--bk-accent);
229
229
  }
230
230
 
231
231
  .comment__replies {
package/define-config.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  // ─────────────────────────────────────────────────────────────
2
2
  // astro-blog-kit · define-config.ts
3
- // Función helper para tipar la config del blog.
4
3
  // ─────────────────────────────────────────────────────────────
5
4
 
6
- import type { BlogKitConfig, BlogTheme } from "./types";
5
+ import type { BlogKitConfig, BlogTheme, BlogHero, BlogUI } from "./types";
7
6
 
8
7
  export interface BlogConfig {
9
8
  /** URL de tu WordPress. Ej: https://cms.tudominio.com */
@@ -14,8 +13,12 @@ export interface BlogConfig {
14
13
  defaultLayout?: "grid" | "magazine" | "featured" | "cards";
15
14
  /** Locale por defecto. @default "en" */
16
15
  locale?: string;
17
- /** Tema visual */
16
+ /** Tema visual (colores, fuentes, tamaños) */
18
17
  theme?: BlogTheme;
18
+ /** Textos del hero/header del blog */
19
+ hero?: BlogHero;
20
+ /** Overrides de UI (botones, paginación, labels) */
21
+ ui?: BlogUI;
19
22
  /** Configuración de i18n */
20
23
  i18n?: {
21
24
  locales: string[];
@@ -25,7 +28,6 @@ export interface BlogConfig {
25
28
 
26
29
  /**
27
30
  * Define la configuración del blog con tipado completo.
28
- * Genera blog.config.ts en la raíz del proyecto.
29
31
  *
30
32
  * @example
31
33
  * ```ts
@@ -35,10 +37,26 @@ export interface BlogConfig {
35
37
  * export default defineBlogConfig({
36
38
  * wpUrl: 'https://cms.tudominio.com',
37
39
  * postsPerPage: 5,
38
- * defaultLayout: 'magazine',
40
+ * defaultLayout: 'featured',
39
41
  * locale: 'en',
40
42
  * theme: {
41
43
  * accent: '#facc15',
44
+ * background: '#0a1a0a',
45
+ * text: '#ffffff',
46
+ * },
47
+ * hero: {
48
+ * tagline: 'Technical Resources',
49
+ * titleLine1: 'Building',
50
+ * titleLine2: 'Insights',
51
+ * description: 'Practical knowledge for architects and engineers.',
52
+ * },
53
+ * ui: {
54
+ * readMoreLabel: 'Read more →',
55
+ * btnPrev: 'Previous',
56
+ * btnNext: 'Next',
57
+ * commentButtonColor: '#facc15',
58
+ * commentButtonTextColor: '#0a0a0a',
59
+ * paginationStyle: 'minimal',
42
60
  * },
43
61
  * });
44
62
  * ```
@@ -60,6 +78,80 @@ export function toBlogKitConfig(config: BlogConfig): BlogKitConfig {
60
78
  postsPerPage: config.postsPerPage,
61
79
  defaultLayout: config.defaultLayout,
62
80
  theme: config.theme,
81
+ hero: config.hero,
82
+ ui: config.ui,
63
83
  i18n: config.i18n,
64
84
  };
85
+ }
86
+
87
+ /**
88
+ * Resuelve los textos del hero con fallbacks según el locale.
89
+ * Usado internamente por BlogList.astro
90
+ */
91
+ export function resolveHero(
92
+ hero: BlogHero | undefined,
93
+ locale: string
94
+ ): Required<BlogHero> {
95
+ const defaults: Record<string, Required<BlogHero>> = {
96
+ en: {
97
+ tagline: "Our Blog",
98
+ titleLine1: "Latest",
99
+ titleLine2: "Articles",
100
+ description: "Welcome to our blog.",
101
+ },
102
+ es: {
103
+ tagline: "Nuestro Blog",
104
+ titleLine1: "Últimos",
105
+ titleLine2: "Artículos",
106
+ description: "Bienvenido a nuestro blog.",
107
+ },
108
+ };
109
+
110
+ const d = defaults[locale] ?? defaults["en"];
111
+
112
+ return {
113
+ tagline: hero?.tagline ?? d.tagline,
114
+ titleLine1: hero?.titleLine1 ?? d.titleLine1,
115
+ titleLine2: hero?.titleLine2 ?? d.titleLine2,
116
+ description: hero?.description ?? d.description,
117
+ };
118
+ }
119
+
120
+ /**
121
+ * Resuelve los labels de UI con fallbacks según el locale.
122
+ * Usado internamente por BlogList.astro y Pagination.astro
123
+ */
124
+ export function resolveUI(
125
+ ui: BlogUI | undefined,
126
+ locale: string
127
+ ): Required<BlogUI> {
128
+ const defaults: Record<string, Required<BlogUI>> = {
129
+ en: {
130
+ readMoreLabel: "Read more →",
131
+ btnPrev: "Previous",
132
+ btnNext: "Next",
133
+ commentButtonColor: "var(--bk-accent)",
134
+ commentButtonTextColor: "var(--bk-black)",
135
+ paginationStyle: "minimal",
136
+ },
137
+ es: {
138
+ readMoreLabel: "Leer más →",
139
+ btnPrev: "Anterior",
140
+ btnNext: "Siguiente",
141
+ commentButtonColor: "var(--bk-accent)",
142
+ commentButtonTextColor: "var(--bk-black)",
143
+ paginationStyle: "minimal",
144
+ },
145
+ };
146
+
147
+ const d = defaults[locale] ?? defaults["en"];
148
+
149
+ return {
150
+ readMoreLabel: ui?.readMoreLabel ?? d.readMoreLabel,
151
+ btnPrev: ui?.btnPrev ?? d.btnPrev,
152
+ btnNext: ui?.btnNext ?? d.btnNext,
153
+ commentButtonColor: ui?.commentButtonColor ?? d.commentButtonColor,
154
+ commentButtonTextColor: ui?.commentButtonTextColor ?? d.commentButtonTextColor,
155
+ paginationStyle: ui?.paginationStyle ?? d.paginationStyle,
156
+ };
65
157
  }
package/integration.ts CHANGED
@@ -3,8 +3,12 @@
3
3
  // ─────────────────────────────────────────────────────────────
4
4
 
5
5
  import type { AstroIntegration } from "astro";
6
+ import type { Plugin } from "vite";
6
7
  import type { BlogKitConfig, BlogTheme } from "./types";
7
8
 
9
+ const VIRTUAL_MODULE_ID = "virtual:astro-blog-kit/theme";
10
+ const RESOLVED_VIRTUAL_MODULE_ID = "\0" + VIRTUAL_MODULE_ID;
11
+
8
12
  /**
9
13
  * Genera el bloque de CSS variables a partir del tema.
10
14
  */
@@ -27,36 +31,79 @@ function generateThemeCSS(theme: BlogTheme = {}): string {
27
31
  };
28
32
 
29
33
  return `
30
- :root {
31
- --bk-accent: ${t.accent};
32
- --bk-background: ${t.background};
33
- --bk-surface: ${t.surface};
34
- --bk-text: ${t.text};
35
- --bk-muted: ${t.muted};
36
- --bk-muted-light: ${t.mutedLight};
37
- --bk-border: ${t.border};
38
- --bk-black: ${t.black};
39
- --bk-white: ${t.white};
40
- --bk-yellow: ${t.accent};
41
- --bk-gray-100: #f3f4f6;
42
- --bk-gray-200: #e5e7eb;
43
- --bk-gray-300: #d1d5db;
44
- --bk-gray-400: #9ca3af;
45
- --bk-gray-600: #4b5563;
46
- --bk-font-heading: ${t.fontHeading};
47
- --bk-font-body: ${t.fontBody};
48
- --bk-font-mono: ${t.fontMono};
49
- --bk-font-display: ${t.fontDisplay};
50
- --bk-container-max: ${t.containerMax};
51
- --bk-transition: all 0.2s ease;
52
- }
34
+ :root {
35
+ --bk-accent: ${t.accent};
36
+ --bk-background: ${t.background};
37
+ --bk-surface: ${t.surface};
38
+ --bk-text: ${t.text};
39
+ --bk-muted: ${t.muted};
40
+ --bk-muted-light: ${t.mutedLight};
41
+ --bk-border: ${t.border};
42
+ --bk-black: ${t.black};
43
+ --bk-white: ${t.white};
44
+ --bk-yellow: ${t.accent};
45
+ --bk-gray-100: #f3f4f6;
46
+ --bk-gray-200: #e5e7eb;
47
+ --bk-gray-300: #d1d5db;
48
+ --bk-gray-400: #9ca3af;
49
+ --bk-gray-600: #4b5563;
50
+ --bk-font-heading: ${t.fontHeading};
51
+ --bk-font-body: ${t.fontBody};
52
+ --bk-font-mono: ${t.fontMono};
53
+ --bk-font-display: ${t.fontDisplay};
54
+ --bk-container-max: ${t.containerMax};
55
+ --bk-transition: all 0.2s ease;
56
+ }
57
+
58
+ *, *::before, *::after {
59
+ box-sizing: border-box;
60
+ margin: 0;
61
+ padding: 0;
62
+ }
63
+ `.trim();
64
+ }
65
+
66
+ /**
67
+ * Plugin de Vite que expone el tema como un virtual module CSS.
68
+ * Esto permite que Astro procese el CSS en SSR correctamente,
69
+ * sin depender de JavaScript en el cliente para inyectar variables.
70
+ *
71
+ * Uso en cualquier componente .astro del paquete:
72
+ * import 'virtual:astro-blog-kit/theme';
73
+ */
74
+ function createThemePlugin(theme: BlogTheme): Plugin {
75
+ const css = generateThemeCSS(theme);
53
76
 
54
- *, *::before, *::after {
55
- box-sizing: border-box;
56
- margin: 0;
57
- padding: 0;
58
- }
59
- `;
77
+ return {
78
+ name: "astro-blog-kit:theme",
79
+ resolveId(id) {
80
+ if (id === VIRTUAL_MODULE_ID) {
81
+ return RESOLVED_VIRTUAL_MODULE_ID;
82
+ }
83
+ },
84
+ load(id) {
85
+ if (id === RESOLVED_VIRTUAL_MODULE_ID) {
86
+ // Retornamos CSS puro — Vite lo procesa como módulo CSS
87
+ return css;
88
+ }
89
+ },
90
+ // Fuerza el tipo del módulo como CSS para que Vite lo trate correctamente
91
+ transform(code, id) {
92
+ if (id === RESOLVED_VIRTUAL_MODULE_ID) {
93
+ return {
94
+ code: `
95
+ const style = document.createElement('style');
96
+ style.id = 'astro-blog-kit-theme';
97
+ style.textContent = ${JSON.stringify(css)};
98
+ if (!document.getElementById('astro-blog-kit-theme')) {
99
+ document.head.appendChild(style);
100
+ }
101
+ `.trim(),
102
+ map: null,
103
+ };
104
+ }
105
+ },
106
+ };
60
107
  }
61
108
 
62
109
  /**
@@ -72,7 +119,7 @@ function generateThemeCSS(theme: BlogTheme = {}): string {
72
119
  * integrations: [
73
120
  * blogKit({
74
121
  * postsPerPage: 6,
75
- * defaultLayout: 'magazine',
122
+ * defaultLayout: 'featured',
76
123
  * theme: {
77
124
  * accent: '#facc15',
78
125
  * fontHeading: 'Inter, sans-serif',
@@ -89,29 +136,33 @@ export function blogKit(config: BlogKitConfig = {}): AstroIntegration {
89
136
  collectionName: config.collectionName ?? "blog",
90
137
  i18n: config.i18n ?? { locales: [], defaultLocale: "en" },
91
138
  theme: config.theme ?? {},
139
+ hero: config.hero ?? {}, // ← agregar
140
+ ui: config.ui ?? {},
92
141
  };
93
142
 
94
143
  return {
95
144
  name: "astro-blog-kit",
96
145
 
97
146
  hooks: {
98
- "astro:config:setup": ({ injectScript, logger }) => {
147
+ "astro:config:setup": ({ updateConfig, injectScript, logger }) => {
99
148
  logger.info(
100
149
  `astro-blog-kit initialized — layout: ${resolvedConfig.defaultLayout}, postsPerPage: ${resolvedConfig.postsPerPage}`
101
150
  );
102
151
 
103
- // Inyecta CSS variables del tema globalmente
104
- const themeCSS = generateThemeCSS(resolvedConfig.theme);
105
- injectScript("head-inline", `
106
- (() => {
107
- const style = document.createElement('style');
108
- style.id = 'astro-blog-kit-theme';
109
- style.textContent = \`${themeCSS.replace(/`/g, "\\`")}\`;
110
- document.head.appendChild(style);
111
- })();
112
- `);
152
+ // Registra el virtual module como plugin de Vite
153
+ // Esto garantiza que las CSS variables existen en SSR y en el build estático
154
+ updateConfig({
155
+ vite: {
156
+ plugins: [createThemePlugin(resolvedConfig.theme)],
157
+ },
158
+ });
159
+
160
+ // Inyecta el virtual module en cada página como CSS real
161
+ // "page-ssr" = se ejecuta en el servidor, garantiza que el style
162
+ // esté disponible antes del primer paint
163
+ injectScript("page-ssr", `import "${VIRTUAL_MODULE_ID}";`);
113
164
 
114
- // Inyecta config global
165
+ // Inyecta config global accesible desde cualquier componente
115
166
  injectScript(
116
167
  "page-ssr",
117
168
  `globalThis.__BLOG_KIT_CONFIG__ = ${JSON.stringify(resolvedConfig)};`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "astro-blog-kit",
3
- "version": "0.2.8",
3
+ "version": "0.3.1",
4
4
  "description": "A ready-to-use blog system for Astro with WordPress headless support, optional i18n, multiple layouts, and a comment system.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,24 +1,41 @@
1
- import { defineBlogConfig } from 'astro-blog-kit';
2
-
3
- export default defineBlogConfig({
4
- wpUrl: import.meta.env.WP_API_URL || '__WP_URL__',
5
- postsPerPage: __POSTS_PER_PAGE__,
6
- defaultLayout: '__DEFAULT_LAYOUT__',
7
- locale: '__LOCALE__',
8
- theme: {
9
- accent: '#facc15',
10
- background: '#ffffff',
11
- surface: '#f8f8f8',
12
- text: '#0a0a0a',
13
- muted: '#6b7280',
14
- mutedLight: '#4b5563',
15
- border: '#e5e7eb',
16
- black: '#0a0a0a',
17
- white: '#ffffff',
18
- fontHeading: 'Georgia, serif',
19
- fontBody: 'system-ui, sans-serif',
20
- fontMono: 'monospace',
21
- fontDisplay: 'Georgia, serif',
22
- containerMax: '1200px',
23
- },
24
- });
1
+ import { defineBlogConfig } from 'astro-blog-kit';
2
+
3
+ export default defineBlogConfig({
4
+ wpUrl: import.meta.env.WP_API_URL || '__WP_URL__',
5
+ postsPerPage: __POSTS_PER_PAGE__,
6
+ defaultLayout: '__DEFAULT_LAYOUT__',
7
+ locale: '__LOCALE__',
8
+
9
+ theme: {
10
+ accent: '#facc15',
11
+ background: '#ffffff',
12
+ surface: '#f8f8f8',
13
+ text: '#0a0a0a',
14
+ muted: '#6b7280',
15
+ mutedLight: '#4b5563',
16
+ border: '#e5e7eb',
17
+ black: '#0a0a0a',
18
+ white: '#ffffff',
19
+ fontHeading: 'Georgia, serif',
20
+ fontBody: 'system-ui, sans-serif',
21
+ fontMono: 'monospace',
22
+ fontDisplay: 'Georgia, serif',
23
+ containerMax:'1200px',
24
+ },
25
+
26
+ hero: {
27
+ tagline: '__T_TAGLINE__',
28
+ titleLine1: '__T_TITLE_LINE1__',
29
+ titleLine2: '__T_TITLE_LINE2__',
30
+ description: '__T_DESCRIPTION__',
31
+ },
32
+
33
+ ui: {
34
+ readMoreLabel: '__T_BTNCTA__',
35
+ btnPrev: '__T_BTN_PREV__',
36
+ btnNext: '__T_BTN_NEXT__',
37
+ commentButtonColor: 'var(--bk-accent)',
38
+ commentButtonTextColor: 'var(--bk-black)',
39
+ paginationStyle: 'minimal',
40
+ },
41
+ });
@@ -1,37 +1,42 @@
1
- ---
2
- import { BlogList } from "astro-blog-kit/components";
3
- import { createWPClient } from "astro-blog-kit/utils";
4
- import config from "../../../blog.config";
5
- __LAYOUT_IMPORT__
6
-
7
- const wp = createWPClient(config.wpUrl);
8
- const { posts, totalPages } = await wp.getPosts({ perPage: config.postsPerPage ?? 5 });
9
-
10
- const bt = {
11
- blog: {
12
- tagline: "__T_TAGLINE__",
13
- title_line1: "__T_TITLE_LINE1__",
14
- title_line2: "__T_TITLE_LINE2__",
15
- description: "__T_DESCRIPTION__",
16
- btncta: "__T_BTNCTA__",
17
- btn_prev: "__T_BTN_PREV__",
18
- btn_next: "__T_BTN_NEXT__",
19
- },
20
- };
21
-
22
- const base = import.meta.env.BASE_URL;
23
- ---
24
-
25
- __LAYOUT_OPEN__
26
- <BlogList
27
- posts={posts}
28
- currentPage={1}
29
- totalPages={totalPages}
30
- basePath={`${base}blog/page/`}
31
- blogBase={`${base}blog/`}
32
- dateLocale={config.locale ?? "en"}
33
- t={bt}
34
- locale={config.locale ?? "en"}
35
- layout={config.defaultLayout ?? "magazine"}
36
- />
37
- __LAYOUT_CLOSE__
1
+ ---
2
+ import { BlogList } from 'astro-blog-kit/components';
3
+ import { createWPClient } from 'astro-blog-kit/utils';
4
+ import { resolveHero, resolveUI } from 'astro-blog-kit';
5
+ import config from '../../../blog.config';
6
+ __LAYOUT_IMPORT__
7
+
8
+ const wp = createWPClient(config.wpUrl);
9
+ const { posts, totalPages } = await wp.getPosts({ perPage: config.postsPerPage ?? 5 });
10
+
11
+ const locale = config.locale ?? 'en';
12
+ const hero = resolveHero(config.hero, locale);
13
+ const ui = resolveUI(config.ui, locale);
14
+
15
+ const t = {
16
+ blog: {
17
+ tagline: hero.tagline,
18
+ title_line1: hero.titleLine1,
19
+ title_line2: hero.titleLine2,
20
+ description: hero.description,
21
+ btncta: ui.readMoreLabel,
22
+ btn_prev: ui.btnPrev,
23
+ btn_next: ui.btnNext,
24
+ },
25
+ };
26
+
27
+ const base = import.meta.env.BASE_URL;
28
+ ---
29
+
30
+ __LAYOUT_OPEN__
31
+ <BlogList
32
+ posts={posts}
33
+ currentPage={1}
34
+ totalPages={totalPages}
35
+ basePath={`${base}blog/page/`}
36
+ blogBase={`${base}blog/`}
37
+ dateLocale={locale}
38
+ t={t}
39
+ locale={locale}
40
+ layout={config.defaultLayout ?? 'magazine'}
41
+ />
42
+ __LAYOUT_CLOSE__
package/types.ts CHANGED
@@ -4,10 +4,6 @@
4
4
 
5
5
  // ── Post ──────────────────────────────────────────────────────
6
6
 
7
- /**
8
- * Forma normalizada de un post de blog.
9
- * Compatible con WordPress REST API (_embedded).
10
- */
11
7
  export interface BlogPost {
12
8
  id?: number;
13
9
  slug: string;
@@ -95,7 +91,7 @@ export interface BlogPostProps {
95
91
  lang: string;
96
92
  }
97
93
 
98
- // ── Config del paquete ────────────────────────────────────────
94
+ // ── Tema ──────────────────────────────────────────────────────
99
95
 
100
96
  export interface BlogTheme {
101
97
  /** Color de acento principal. @default "#facc15" */
@@ -128,6 +124,38 @@ export interface BlogTheme {
128
124
  containerMax?: string;
129
125
  }
130
126
 
127
+ // ── Hero del blog ─────────────────────────────────────────────
128
+
129
+ export interface BlogHero {
130
+ /** Texto del badge superior. @default "Our Blog" */
131
+ tagline?: string;
132
+ /** Primera línea del título. @default "Latest" */
133
+ titleLine1?: string;
134
+ /** Segunda línea del título (resaltada). @default "Articles" */
135
+ titleLine2?: string;
136
+ /** Párrafo descriptivo debajo del título. @default "Welcome to our blog." */
137
+ description?: string;
138
+ }
139
+
140
+ // ── UI overrides ──────────────────────────────────────────────
141
+
142
+ export interface BlogUI {
143
+ /** Texto del botón "leer más". @default "Read more →" */
144
+ readMoreLabel?: string;
145
+ /** Texto del botón de página anterior. @default "Previous" */
146
+ btnPrev?: string;
147
+ /** Texto del botón de página siguiente. @default "Next" */
148
+ btnNext?: string;
149
+ /** Color de fondo del botón de comentarios. @default var(--bk-accent) */
150
+ commentButtonColor?: string;
151
+ /** Color del texto del botón de comentarios. @default var(--bk-black) */
152
+ commentButtonTextColor?: string;
153
+ /** Estilo de paginación. @default "minimal" */
154
+ paginationStyle?: "minimal" | "numbered";
155
+ }
156
+
157
+ // ── Config del paquete ────────────────────────────────────────
158
+
131
159
  export interface BlogKitConfig {
132
160
  /** @default 5 */
133
161
  postsPerPage?: number;
@@ -138,6 +166,10 @@ export interface BlogKitConfig {
138
166
  collectionName?: string;
139
167
  /** Tema visual del blog */
140
168
  theme?: BlogTheme;
169
+ /** Textos del hero/header del blog */
170
+ hero?: BlogHero;
171
+ /** Overrides de UI (botones, paginación, labels) */
172
+ ui?: BlogUI;
141
173
  }
142
174
 
143
175
  // ── getStaticPaths ────────────────────────────────────────────