wpheadless-lib 1.1.10 → 1.1.11

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.
Files changed (119) hide show
  1. package/dist/api/categories.d.ts +1 -1
  2. package/dist/api/categories.js +18 -2
  3. package/dist/api/index.d.ts +7 -6
  4. package/dist/api/index.js +7 -6
  5. package/dist/api/mappers.d.ts +1 -1
  6. package/dist/api/mappers.js +1 -1
  7. package/dist/api/menus.d.ts +18 -0
  8. package/dist/api/menus.js +39 -0
  9. package/dist/api/posts.js +30 -2
  10. package/dist/api/translations.js +7 -1
  11. package/dist/api/wpClient.d.ts +5 -8
  12. package/dist/api/wpClient.js +14 -0
  13. package/dist/app/config.d.ts +2 -1
  14. package/dist/app/config.js +4 -0
  15. package/dist/app/globals.css +33 -121
  16. package/dist/base/category/index.d.ts +3 -3
  17. package/dist/base/category/index.js +9 -8
  18. package/dist/base/category/pagination.d.ts +2 -2
  19. package/dist/base/category/pagination.js +12 -10
  20. package/dist/base/home/index.js +1 -1
  21. package/dist/base/home/pagination.js +3 -3
  22. package/dist/base/index.d.ts +7 -7
  23. package/dist/base/index.js +7 -7
  24. package/dist/base/layout/RootLayoutBase.d.ts +6 -7
  25. package/dist/base/layout/RootLayoutBase.js +4 -4
  26. package/dist/base/layout/RootLayoutBase.module.css +10 -0
  27. package/dist/base/legal/index.d.ts +4 -4
  28. package/dist/base/legal/index.js +36 -24
  29. package/dist/base/post/index.d.ts +1 -1
  30. package/dist/base/post/index.js +15 -12
  31. package/dist/components/Breadcrumbs/index.js +4 -4
  32. package/dist/components/JsonLd.d.ts +5 -0
  33. package/dist/components/JsonLd.js +9 -0
  34. package/dist/components/Paginator/index.d.ts +1 -1
  35. package/dist/components/Paginator/index.js +5 -5
  36. package/dist/components/PostCard/index.d.ts +3 -2
  37. package/dist/components/PostCard/index.js +8 -6
  38. package/dist/components/PostCard/index.module.css +7 -7
  39. package/dist/components/PostCard/index.types.d.ts +2 -1
  40. package/dist/components/PostCard/index.utils.d.ts +5 -5
  41. package/dist/components/PostCard/index.utils.js +8 -6
  42. package/dist/components/index.d.ts +6 -5
  43. package/dist/components/index.js +6 -5
  44. package/dist/components/layout/Footer/index.d.ts +5 -5
  45. package/dist/components/layout/Footer/index.js +11 -22
  46. package/dist/components/layout/Footer/index.module.css +59 -9
  47. package/dist/components/layout/Header/HeaderClient.d.ts +1 -1
  48. package/dist/components/layout/Header/HeaderClient.js +22 -2
  49. package/dist/components/layout/Header/index.d.ts +5 -5
  50. package/dist/components/layout/Header/index.js +10 -29
  51. package/dist/components/layout/Header/index.module.css +77 -16
  52. package/dist/components/ui/alert.d.ts +8 -0
  53. package/dist/components/ui/alert.js +12 -0
  54. package/dist/components/ui/alert.module.css +26 -0
  55. package/dist/components/ui/badge.d.ts +3 -0
  56. package/dist/components/ui/badge.js +9 -0
  57. package/dist/components/ui/badge.module.css +23 -0
  58. package/dist/components/ui/breadcrumb.d.ts +8 -0
  59. package/dist/components/ui/breadcrumb.js +22 -0
  60. package/dist/components/ui/breadcrumb.module.css +37 -0
  61. package/dist/components/ui/button.d.ts +7 -0
  62. package/dist/components/ui/button.js +16 -0
  63. package/dist/components/ui/button.module.css +72 -0
  64. package/dist/components/ui/card.d.ts +11 -0
  65. package/dist/components/ui/card.js +16 -0
  66. package/dist/components/ui/card.module.css +37 -0
  67. package/dist/components/ui/cn.d.ts +1 -0
  68. package/dist/components/ui/cn.js +3 -0
  69. package/dist/components/ui/index.d.ts +6 -0
  70. package/dist/components/ui/index.js +6 -0
  71. package/dist/components/ui/pagination.d.ts +14 -0
  72. package/dist/components/ui/pagination.js +25 -0
  73. package/dist/components/ui/pagination.module.css +62 -0
  74. package/dist/index.d.ts +6 -6
  75. package/dist/index.js +6 -6
  76. package/dist/plugins/index.d.ts +1 -1
  77. package/dist/plugins/index.js +1 -1
  78. package/dist/plugins/yoast/index.js +10 -7
  79. package/dist/utils/hreflang.d.ts +4 -7
  80. package/dist/utils/hreflang.js +11 -6
  81. package/dist/utils/html.d.ts +2 -0
  82. package/dist/utils/html.js +25 -0
  83. package/dist/utils/index.d.ts +7 -6
  84. package/dist/utils/index.js +7 -6
  85. package/dist/utils/language.d.ts +37 -0
  86. package/dist/utils/language.js +75 -0
  87. package/dist/utils/seo.d.ts +1 -6
  88. package/dist/utils/seo.js +9 -5
  89. package/dist/utils/sitemap.d.ts +1 -1
  90. package/dist/utils/sitemap.js +107 -56
  91. package/dist/views/CategoryListView/index.d.ts +1 -1
  92. package/dist/views/CategoryListView/index.js +6 -6
  93. package/dist/views/CategoryListView/index.utils.d.ts +1 -1
  94. package/dist/views/CategoryListView/index.utils.js +1 -1
  95. package/dist/views/CategoryPaginationView/index.d.ts +1 -1
  96. package/dist/views/CategoryPaginationView/index.js +6 -6
  97. package/dist/views/CategoryPaginationView/index.utils.d.ts +1 -1
  98. package/dist/views/CategoryPaginationView/index.utils.js +1 -1
  99. package/dist/views/HomePaginationView/index.d.ts +1 -1
  100. package/dist/views/HomePaginationView/index.js +5 -4
  101. package/dist/views/HomePaginationView/index.utils.d.ts +1 -1
  102. package/dist/views/HomePaginationView/index.utils.js +1 -1
  103. package/dist/views/HomeView/index.d.ts +1 -1
  104. package/dist/views/HomeView/index.js +13 -12
  105. package/dist/views/HomeView/index.module.css +51 -8
  106. package/dist/views/HomeView/index.utils.d.ts +1 -1
  107. package/dist/views/HomeView/index.utils.js +2 -2
  108. package/dist/views/LegalPageView/index.d.ts +2 -1
  109. package/dist/views/LegalPageView/index.js +5 -5
  110. package/dist/views/LegalPageView/index.module.css +3 -3
  111. package/dist/views/LegalPageView/index.utils.js +1 -1
  112. package/dist/views/PostView/index.d.ts +3 -2
  113. package/dist/views/PostView/index.js +9 -7
  114. package/dist/views/PostView/index.module.css +46 -13
  115. package/dist/views/PostView/index.utils.d.ts +3 -3
  116. package/dist/views/PostView/index.utils.js +2 -2
  117. package/dist/views/index.d.ts +6 -6
  118. package/dist/views/index.js +6 -6
  119. package/package.json +47 -10
@@ -1,5 +1,5 @@
1
1
  import type { WPCategory, WPPost } from "wpjsapi-lib";
2
- import type { ApiResult } from "./posts";
2
+ import type { ApiResult } from "./posts.js";
3
3
  export type CategoryClientConfig = {
4
4
  baseApiUrl: string;
5
5
  username?: string;
@@ -1,6 +1,9 @@
1
- import { createWpClient } from "./wpClient";
2
- import { fetchCategoriesByTranslationKey } from "./translations";
1
+ import { createWpClient, isValidWpApiUrl } from "./wpClient.js";
2
+ import { fetchCategoriesByTranslationKey } from "./translations.js";
3
3
  export async function listCategoryStaticParams({ baseApiUrl, lang, }) {
4
+ if (!isValidWpApiUrl(baseApiUrl)) {
5
+ return { ok: true, data: [], error: null };
6
+ }
4
7
  try {
5
8
  const { categoriesApi } = createWpClient({
6
9
  baseUrl: baseApiUrl,
@@ -21,6 +24,9 @@ export async function listCategoryStaticParams({ baseApiUrl, lang, }) {
21
24
  }
22
25
  }
23
26
  export async function getCategoryBySlug({ baseApiUrl, slug, lang, }) {
27
+ if (!isValidWpApiUrl(baseApiUrl)) {
28
+ return { ok: true, data: null, error: null };
29
+ }
24
30
  try {
25
31
  const { categoriesApi } = createWpClient({
26
32
  baseUrl: baseApiUrl,
@@ -42,6 +48,13 @@ export async function getCategoryBySlug({ baseApiUrl, slug, lang, }) {
42
48
  }
43
49
  }
44
50
  export async function listCategoryPosts({ baseApiUrl, categoryId, perPage, langId, }) {
51
+ if (!isValidWpApiUrl(baseApiUrl)) {
52
+ return {
53
+ ok: true,
54
+ data: { posts: [], totalPages: 0 },
55
+ error: null,
56
+ };
57
+ }
45
58
  try {
46
59
  const { postsApi } = createWpClient({
47
60
  baseUrl: baseApiUrl,
@@ -75,6 +88,9 @@ export async function listCategoryPosts({ baseApiUrl, categoryId, perPage, langI
75
88
  }
76
89
  }
77
90
  export async function getCategoryByTranslationKey({ baseApiUrl, translationKey, lang, perPage = 1, }) {
91
+ if (!isValidWpApiUrl(baseApiUrl)) {
92
+ return { ok: true, data: null, error: null };
93
+ }
78
94
  try {
79
95
  const categories = await fetchCategoriesByTranslationKey({
80
96
  baseApiUrl,
@@ -1,6 +1,7 @@
1
- export * from "./types";
2
- export * from "./posts";
3
- export * from "./categories";
4
- export * from "./translations";
5
- export * from "./mappers";
6
- export * from "./wpClient";
1
+ export * from "./types.js";
2
+ export * from "./posts.js";
3
+ export * from "./categories.js";
4
+ export * from "./translations.js";
5
+ export * from "./mappers.js";
6
+ export * from "./wpClient.js";
7
+ export * from "./menus.js";
package/dist/api/index.js CHANGED
@@ -1,6 +1,7 @@
1
- export * from "./types";
2
- export * from "./posts";
3
- export * from "./categories";
4
- export * from "./translations";
5
- export * from "./mappers";
6
- export * from "./wpClient";
1
+ export * from "./types.js";
2
+ export * from "./posts.js";
3
+ export * from "./categories.js";
4
+ export * from "./translations.js";
5
+ export * from "./mappers.js";
6
+ export * from "./wpClient.js";
7
+ export * from "./menus.js";
@@ -1,5 +1,5 @@
1
1
  import type { WPCategory, WPPost, WPPage } from "wpjsapi-lib";
2
- import { type CategoryEntity, type PostEntity, type PageEntity } from "./types";
2
+ import { type CategoryEntity, type PostEntity, type PageEntity } from "./types.js";
3
3
  export declare function mapPostEntity(post: WPPost): PostEntity;
4
4
  export declare function mapCategoryEntity(category: WPCategory): CategoryEntity;
5
5
  export declare function mapPageEntity(page: WPPage): PageEntity<WPPage>;
@@ -1,4 +1,4 @@
1
- import { getEntityLanguageId, getTranslationKey } from "../utils/hreflang";
1
+ import { getEntityLanguageId, getTranslationKey } from "../utils/hreflang.js";
2
2
  const getEmbeddedCategories = (post) => (post._embedded?.["wp:term"]?.[0] || []).filter(Boolean);
3
3
  export function mapPostEntity(post) {
4
4
  return {
@@ -0,0 +1,18 @@
1
+ import { type WPMenuItem } from "wpjsapi-lib";
2
+ export type MenuClientConfig = {
3
+ baseApiUrl: string;
4
+ username?: string;
5
+ appPassword?: string;
6
+ };
7
+ export type MenuLink = {
8
+ id: number;
9
+ href: string;
10
+ label: string;
11
+ target?: string;
12
+ };
13
+ export declare function fetchMenuItems({ baseApiUrl, menuId, username, appPassword, }: MenuClientConfig & {
14
+ menuId?: string | number;
15
+ }): Promise<WPMenuItem[]>;
16
+ export declare function fetchMenuLinks(params: MenuClientConfig & {
17
+ menuId?: string | number;
18
+ }): Promise<MenuLink[]>;
@@ -0,0 +1,39 @@
1
+ import { createAuth, createMenuEndpoints } from "wpjsapi-lib";
2
+ import { convertWpUrl } from "../utils/routing.js";
3
+ import { isValidWpApiUrl } from "./wpClient.js";
4
+ export async function fetchMenuItems({ baseApiUrl, menuId, username, appPassword, }) {
5
+ if (!isValidWpApiUrl(baseApiUrl) || !menuId)
6
+ return [];
7
+ const parsedMenuId = typeof menuId === "number" ? menuId : Number.parseInt(menuId, 10);
8
+ if (Number.isNaN(parsedMenuId))
9
+ return [];
10
+ const auth = username && appPassword
11
+ ? createAuth({
12
+ method: "basic",
13
+ credentials: {
14
+ username,
15
+ password: appPassword,
16
+ },
17
+ })
18
+ : undefined;
19
+ const menusApi = createMenuEndpoints({
20
+ baseUrl: baseApiUrl,
21
+ ...(auth ? { auth } : {}),
22
+ });
23
+ const itemsResponse = await menusApi.items.list({
24
+ menus: [parsedMenuId],
25
+ per_page: 100,
26
+ orderby: "menu_order",
27
+ order: "asc",
28
+ });
29
+ return itemsResponse.items;
30
+ }
31
+ export async function fetchMenuLinks(params) {
32
+ const items = await fetchMenuItems(params);
33
+ return items.map((item) => ({
34
+ id: item.id,
35
+ href: convertWpUrl(item.url, params.baseApiUrl),
36
+ label: item.title.rendered,
37
+ target: item.target || undefined,
38
+ }));
39
+ }
package/dist/api/posts.js CHANGED
@@ -1,7 +1,22 @@
1
- import { createWpClient } from "./wpClient";
2
- import { fetchPostsByTranslationKey } from "./translations";
1
+ import { createWpClient, isValidWpApiUrl } from "./wpClient.js";
2
+ import { fetchPostsByTranslationKey } from "./translations.js";
3
3
  const toMessage = (err) => err instanceof Error ? err.message : "Unknown error";
4
+ function emptyPaginatedPosts(page = 1, perPage = 0) {
5
+ return {
6
+ items: [],
7
+ pagination: {
8
+ total: 0,
9
+ totalPages: 0,
10
+ currentPage: page,
11
+ perPage,
12
+ hasMore: false,
13
+ },
14
+ };
15
+ }
4
16
  export async function getPostBySlug({ baseApiUrl, slug, langId, username, appPassword, }) {
17
+ if (!isValidWpApiUrl(baseApiUrl)) {
18
+ return { ok: true, data: null, error: null };
19
+ }
5
20
  try {
6
21
  const { postsApi } = createWpClient({
7
22
  baseUrl: baseApiUrl,
@@ -24,6 +39,9 @@ export async function getPostBySlug({ baseApiUrl, slug, langId, username, appPas
24
39
  }
25
40
  }
26
41
  export async function listPostStaticParams({ baseApiUrl, lang, langId, }) {
42
+ if (!isValidWpApiUrl(baseApiUrl)) {
43
+ return { ok: true, data: [], error: null };
44
+ }
27
45
  try {
28
46
  const { postsApi, categoriesApi } = createWpClient({ baseUrl: baseApiUrl });
29
47
  const categories = await categoriesApi.listAll(lang ? { lang } : undefined);
@@ -54,6 +72,9 @@ export async function listPostStaticParams({ baseApiUrl, lang, langId, }) {
54
72
  }
55
73
  }
56
74
  export async function listPostsByTranslationKey({ baseApiUrl, translationKey, username, appPassword, perPage = 20, }) {
75
+ if (!isValidWpApiUrl(baseApiUrl)) {
76
+ return { ok: true, data: [], error: null };
77
+ }
57
78
  try {
58
79
  const posts = await fetchPostsByTranslationKey({
59
80
  baseApiUrl,
@@ -69,6 +90,13 @@ export async function listPostsByTranslationKey({ baseApiUrl, translationKey, us
69
90
  }
70
91
  }
71
92
  export async function listPosts({ baseApiUrl, perPage, page, langId, categories, embed = false, fields, order, orderby, }) {
93
+ if (!isValidWpApiUrl(baseApiUrl)) {
94
+ return {
95
+ ok: true,
96
+ data: emptyPaginatedPosts(page ?? 1, perPage),
97
+ error: null,
98
+ };
99
+ }
72
100
  try {
73
101
  const { postsApi } = createWpClient({
74
102
  baseUrl: baseApiUrl,
@@ -1,5 +1,8 @@
1
- import { createWpClient } from "./wpClient";
1
+ import { createWpClient, isValidWpApiUrl } from "./wpClient.js";
2
2
  export async function fetchPostsByTranslationKey({ baseApiUrl, translationKey, username, appPassword, perPage = 20, }) {
3
+ if (!isValidWpApiUrl(baseApiUrl)) {
4
+ return [];
5
+ }
3
6
  const { postsApi } = createWpClient({
4
7
  baseUrl: baseApiUrl,
5
8
  username,
@@ -13,6 +16,9 @@ export async function fetchPostsByTranslationKey({ baseApiUrl, translationKey, u
13
16
  return response.items;
14
17
  }
15
18
  export async function fetchCategoriesByTranslationKey({ baseApiUrl, translationKey, lang, username, appPassword, perPage = 20, }) {
19
+ if (!isValidWpApiUrl(baseApiUrl)) {
20
+ return [];
21
+ }
16
22
  const { categoriesApi } = createWpClient({
17
23
  baseUrl: baseApiUrl,
18
24
  username,
@@ -3,13 +3,12 @@ type WpClientOptions = {
3
3
  username?: string;
4
4
  appPassword?: string;
5
5
  };
6
+ export declare function isValidWpApiUrl(baseUrl?: string): baseUrl is string;
6
7
  export declare function createWpClient({ baseUrl, username, appPassword, }: WpClientOptions): {
7
8
  postsApi: {
9
+ listAll: (params?: Omit<import("wpjsapi-lib").WPPostParameters, "page" | "per_page"> | undefined, options?: import("wpjsapi-lib").RequestOptions) => Promise<import("wpjsapi-lib").WPPost[]>;
10
+ pages: (params?: import("wpjsapi-lib").WPPostParameters | undefined, options?: import("wpjsapi-lib").RequestOptions) => AsyncGenerator<import("wpjsapi-lib").WPPaginatedResponse<import("wpjsapi-lib").WPPost>, void, unknown>;
8
11
  list: (params?: import("wpjsapi-lib").WPPostParameters, options?: import("wpjsapi-lib").RequestOptions) => Promise<import("wpjsapi-lib").WPPaginatedResponse<import("wpjsapi-lib").WPPost>>;
9
- listAll: (params?: Omit<import("wpjsapi-lib").WPPostParameters, "page" | "per_page">, options?: import("wpjsapi-lib").RequestOptions) => Promise<import("wpjsapi-lib").WPPost[]>;
10
- pages: (params?: import("wpjsapi-lib").WPPostParameters, options?: import("wpjsapi-lib").RequestOptions) => {
11
- [Symbol.asyncIterator](): AsyncGenerator<import("wpjsapi-lib").WPPaginatedResponse<import("wpjsapi-lib").WPPost>, void, unknown>;
12
- };
13
12
  get: (id: number, context?: "view" | "embed" | "edit", embed?: boolean, options?: import("wpjsapi-lib").RequestOptions) => Promise<import("wpjsapi-lib").WPPost>;
14
13
  create: (data: import("wpjsapi-lib").WPPostCreate, options?: import("wpjsapi-lib").RequestOptions) => Promise<import("wpjsapi-lib").WPPost>;
15
14
  update: (id: number, data: import("wpjsapi-lib").WPPostUpdate, options?: import("wpjsapi-lib").RequestOptions) => Promise<import("wpjsapi-lib").WPPost>;
@@ -17,11 +16,9 @@ export declare function createWpClient({ baseUrl, username, appPassword, }: WpCl
17
16
  getRevisions: (id: number, options?: import("wpjsapi-lib").RequestOptions) => Promise<import("wpjsapi-lib").WPPost[]>;
18
17
  };
19
18
  categoriesApi: {
19
+ listAll: (params?: Omit<import("wpjsapi-lib").WPCategoryParameters, "page" | "per_page"> | undefined, options?: import("wpjsapi-lib").RequestOptions) => Promise<import("wpjsapi-lib").WPCategory[]>;
20
+ pages: (params?: import("wpjsapi-lib").WPCategoryParameters | undefined, options?: import("wpjsapi-lib").RequestOptions) => AsyncGenerator<import("wpjsapi-lib").WPPaginatedResponse<import("wpjsapi-lib").WPCategory>, void, unknown>;
20
21
  list: (params?: import("wpjsapi-lib").WPCategoryParameters, options?: import("wpjsapi-lib").RequestOptions) => Promise<import("wpjsapi-lib").WPPaginatedResponse<import("wpjsapi-lib").WPCategory>>;
21
- listAll: (params?: Omit<import("wpjsapi-lib").WPCategoryParameters, "page" | "per_page">, options?: import("wpjsapi-lib").RequestOptions) => Promise<import("wpjsapi-lib").WPCategory[]>;
22
- pages: (params?: import("wpjsapi-lib").WPCategoryParameters, options?: import("wpjsapi-lib").RequestOptions) => {
23
- [Symbol.asyncIterator](): AsyncGenerator<import("wpjsapi-lib").WPPaginatedResponse<import("wpjsapi-lib").WPCategory>, void, unknown>;
24
- };
25
22
  get: (id: number, context?: "view" | "embed" | "edit", embed?: boolean, options?: import("wpjsapi-lib").RequestOptions) => Promise<import("wpjsapi-lib").WPCategory>;
26
23
  create: (data: import("wpjsapi-lib").WPCategoryCreate, options?: import("wpjsapi-lib").RequestOptions) => Promise<import("wpjsapi-lib").WPCategory>;
27
24
  update: (id: number, data: import("wpjsapi-lib").WPCategoryUpdate, options?: import("wpjsapi-lib").RequestOptions) => Promise<import("wpjsapi-lib").WPCategory>;
@@ -1,5 +1,19 @@
1
1
  import { createAuth, createCategoryEndpoints, createPostsEndpoints, } from "wpjsapi-lib";
2
+ export function isValidWpApiUrl(baseUrl) {
3
+ if (!baseUrl)
4
+ return false;
5
+ try {
6
+ const url = new URL(baseUrl);
7
+ return url.protocol === "http:" || url.protocol === "https:";
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ }
2
13
  export function createWpClient({ baseUrl, username, appPassword, }) {
14
+ if (!isValidWpApiUrl(baseUrl)) {
15
+ throw new Error("WP API URL must be an absolute http(s) URL. Set WP_API_URL to your WordPress REST API root.");
16
+ }
3
17
  const hasAuth = username && appPassword;
4
18
  const auth = hasAuth
5
19
  ? createAuth({
@@ -1,5 +1,6 @@
1
- import type { LanguageConfig } from "../utils/hreflang";
1
+ import type { LanguageConfig } from "../utils/hreflang.js";
2
2
  export declare const languages: LanguageConfig[];
3
+ export declare const normalizedLanguages: import("../index.js").SiteLanguage[];
3
4
  export declare const runtimeConfig: {
4
5
  postsPerPagePagination: number;
5
6
  };
@@ -1,3 +1,4 @@
1
+ import { normalizeLanguages } from "../utils/language.js";
1
2
  const parseLanguagesFromEnv = () => {
2
3
  const raw = process.env.WP_LANGUAGES;
3
4
  if (!raw)
@@ -23,6 +24,9 @@ const parseLanguagesFromEnv = () => {
23
24
  }
24
25
  };
25
26
  export const languages = parseLanguagesFromEnv();
27
+ export const normalizedLanguages = normalizeLanguages(languages, {
28
+ defaultCode: process.env.WP_DEFAULT_LANGUAGE || process.env.NEXT_PUBLIC_DEFAULT_LANGUAGE,
29
+ });
26
30
  export const runtimeConfig = {
27
31
  postsPerPagePagination: Number.parseInt(process.env.WP_POSTS_PER_PAGE_PAGINATION ?? "", 10) || 10,
28
32
  };
@@ -13,11 +13,15 @@
13
13
  --font-display: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
14
14
 
15
15
  /* Transitions */
16
- --transition: all 0.2s ease;
16
+ --transition: color 0.2s ease, background-color 0.2s ease,
17
+ border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
18
+ --focus-ring: 0 0 0 3px rgba(37, 99, 235, 0.28);
17
19
 
18
20
  /* Colors - Light theme (default) */
19
21
  --primary: #2563eb;
20
22
  --primary-hover: #1d4ed8;
23
+ --primary-foreground: #ffffff;
24
+ --destructive: #dc2626;
21
25
  --secondary: #64748b;
22
26
  --background: #ffffff;
23
27
  --background-alt: #f8fafc;
@@ -65,6 +69,21 @@ html {
65
69
  scroll-behavior: smooth;
66
70
  }
67
71
 
72
+ @media (prefers-reduced-motion: reduce) {
73
+ html {
74
+ scroll-behavior: auto;
75
+ }
76
+
77
+ *,
78
+ *::before,
79
+ *::after {
80
+ animation-duration: 0.01ms !important;
81
+ animation-iteration-count: 1 !important;
82
+ scroll-behavior: auto !important;
83
+ transition-duration: 0.01ms !important;
84
+ }
85
+ }
86
+
68
87
  html,
69
88
  body {
70
89
  max-width: 100vw;
@@ -78,6 +97,7 @@ body {
78
97
  -webkit-font-smoothing: antialiased;
79
98
  -moz-osx-font-smoothing: grayscale;
80
99
  line-height: 1.6;
100
+ touch-action: manipulation;
81
101
  }
82
102
 
83
103
  /* Typography */
@@ -92,6 +112,7 @@ h6 {
92
112
  line-height: 1.2;
93
113
  color: var(--foreground);
94
114
  margin-bottom: 0.75rem;
115
+ text-wrap: balance;
95
116
  }
96
117
 
97
118
  h1 {
@@ -122,6 +143,17 @@ a:hover {
122
143
  color: var(--primary-hover);
123
144
  }
124
145
 
146
+ a:focus-visible,
147
+ button:focus-visible,
148
+ [role="button"]:focus-visible,
149
+ input:focus-visible,
150
+ select:focus-visible,
151
+ textarea:focus-visible {
152
+ outline: 2px solid var(--primary);
153
+ outline-offset: 3px;
154
+ box-shadow: var(--focus-ring);
155
+ }
156
+
125
157
  /* Post content styling */
126
158
  .post-content h2 {
127
159
  margin-top: 2.5rem;
@@ -196,51 +228,6 @@ a:hover {
196
228
  overflow-x: auto;
197
229
  }
198
230
 
199
- /* Container */
200
- .container {
201
- width: 100%;
202
- max-width: var(--container-width);
203
- margin: 0 auto;
204
- padding: 0 var(--spacing-md);
205
- }
206
-
207
- /* Card styling */
208
- .card {
209
- background: var(--card-bg);
210
- border-radius: 12px;
211
- overflow: hidden;
212
- box-shadow: 0 1px 3px var(--shadow);
213
- transition: var(--transition);
214
- }
215
-
216
- .card:hover {
217
- box-shadow: 0 10px 25px var(--shadow-hover);
218
- transform: translateY(-2px);
219
- }
220
-
221
- /* Badge */
222
- .badge {
223
- display: inline-block;
224
- padding: 0.25rem 0.75rem;
225
- background: var(--primary);
226
- color: #ffffff;
227
- font-size: 0.75rem;
228
- font-weight: 600;
229
- border-radius: 9999px;
230
- text-transform: uppercase;
231
- letter-spacing: 0.05em;
232
- transition: var(--transition);
233
- }
234
-
235
- .badge:hover,
236
- .badge:focus-visible,
237
- a.badge,
238
- a.badge:hover,
239
- a.badge:focus-visible {
240
- color: #ffffff !important;
241
- background: var(--primary-hover);
242
- }
243
-
244
231
  /* Utilities */
245
232
  .text-muted {
246
233
  color: var(--foreground-light);
@@ -250,45 +237,6 @@ a {
250
237
  text-decoration: none;
251
238
  }
252
239
 
253
- a.header-link {
254
- color: var(--foreground);
255
- font-weight: 600;
256
- font-size: 0.9375rem;
257
- transition: var(--transition);
258
- position: relative;
259
- display: inline-block;
260
- }
261
-
262
- a.header-link::after {
263
- content: "";
264
- position: absolute;
265
- bottom: -4px;
266
- left: 0;
267
- width: 0;
268
- height: 2px;
269
- background: var(--primary);
270
- transition: width 0.3s ease;
271
- }
272
-
273
- a.header-link:hover {
274
- color: var(--primary);
275
- }
276
-
277
- a.header-link:hover::after {
278
- width: 100%;
279
- }
280
-
281
- a.footer-link {
282
- color: var(--foreground-light);
283
- font-size: 0.875rem;
284
- transition: var(--transition);
285
- display: inline-block;
286
- }
287
-
288
- a.footer-link:hover {
289
- color: var(--primary);
290
- }
291
-
292
240
  @media (prefers-color-scheme: dark) {
293
241
  :root:not([data-theme]) {
294
242
  color-scheme: dark;
@@ -298,39 +246,3 @@ a.footer-link:hover {
298
246
  :root[data-theme="dark"] {
299
247
  color-scheme: dark;
300
248
  }
301
-
302
- /* Paginator styles */
303
- .paginator-nav,
304
- .paginator-page {
305
- padding: 0.625rem 1.25rem;
306
- border: 1px solid var(--border);
307
- border-radius: 8px;
308
- background: var(--card-bg);
309
- color: var(--foreground);
310
- font-weight: 500;
311
- font-size: 0.9375rem;
312
- transition: var(--transition);
313
- display: inline-flex;
314
- align-items: center;
315
- justify-content: center;
316
- min-width: 44px;
317
- }
318
-
319
- .paginator-nav:hover,
320
- .paginator-page:hover {
321
- border-color: var(--primary);
322
- background: var(--background-alt);
323
- color: var(--primary);
324
- }
325
-
326
- .paginator-page.paginator-active {
327
- background: var(--primary);
328
- color: white;
329
- border-color: var(--primary);
330
- font-weight: 600;
331
- }
332
-
333
- .paginator-page.paginator-active:hover {
334
- background: var(--primary-hover);
335
- color: white;
336
- }
@@ -2,7 +2,7 @@ import { type WPCategory, type WPPost } from "wpjsapi-lib";
2
2
  import type { Metadata } from "next";
3
3
  type CategoryStaticParamsInput = {
4
4
  baseApiUrl: string;
5
- lang: string;
5
+ lang?: string;
6
6
  };
7
7
  export declare function getCategoryStaticParams({ baseApiUrl, lang, }: CategoryStaticParamsInput): Promise<Array<{
8
8
  category: string;
@@ -10,7 +10,7 @@ export declare function getCategoryStaticParams({ baseApiUrl, lang, }: CategoryS
10
10
  export declare function fetchCategory({ baseApiUrl, slug, lang, }: {
11
11
  baseApiUrl: string;
12
12
  slug: string;
13
- lang: string;
13
+ lang?: string;
14
14
  }): Promise<WPCategory | null>;
15
15
  export declare function fetchCategoryPosts({ baseApiUrl, categoryId, perPage, langId, }: {
16
16
  baseApiUrl: string;
@@ -24,7 +24,7 @@ export declare function fetchCategoryPosts({ baseApiUrl, categoryId, perPage, la
24
24
  export declare function fetchCategoryByTranslationKey({ baseApiUrl, translationKey, lang, }: {
25
25
  baseApiUrl: string;
26
26
  translationKey: string;
27
- lang: string;
27
+ lang?: string;
28
28
  }): Promise<WPCategory | null>;
29
29
  export declare function buildCategoryMetadata({ category, canonical, useYoast, siteName, languages, siteUrl, wpApiUrl, }: {
30
30
  category: WPCategory;
@@ -1,7 +1,7 @@
1
- import { getPlainTextExcerpt } from "../../utils";
2
- import { withXDefault } from "../../utils/hreflang";
3
- import { extractYoastMetadata } from "../../plugins/yoast";
4
- import { getCategoryByTranslationKey, getCategoryBySlug, listCategoryPosts, listCategoryStaticParams, } from "../../api/categories";
1
+ import { getPlainTextExcerpt, toPlainText } from "../../utils/index.js";
2
+ import { withXDefault } from "../../utils/hreflang.js";
3
+ import { extractYoastMetadata } from "../../plugins/yoast/index.js";
4
+ import { getCategoryByTranslationKey, getCategoryBySlug, listCategoryPosts, listCategoryStaticParams, } from "../../api/categories.js";
5
5
  export async function getCategoryStaticParams({ baseApiUrl, lang, }) {
6
6
  const result = await listCategoryStaticParams({
7
7
  baseApiUrl,
@@ -57,16 +57,17 @@ export async function fetchCategoryByTranslationKey({ baseApiUrl, translationKey
57
57
  }
58
58
  export function buildCategoryMetadata({ category, canonical, useYoast = true, siteName, languages, siteUrl, wpApiUrl, }) {
59
59
  const fallbackDescription = getPlainTextExcerpt(category.description, 160);
60
+ const fallbackTitle = toPlainText(category.name) || category.name;
60
61
  const languageAlternates = withXDefault(languages);
61
62
  const fallbackMetadata = {
62
- title: category.name,
63
+ title: fallbackTitle,
63
64
  description: fallbackDescription,
64
65
  alternates: {
65
66
  canonical,
66
67
  ...(languageAlternates ? { languages: languageAlternates } : {}),
67
68
  },
68
69
  openGraph: {
69
- title: category.name,
70
+ title: fallbackTitle,
70
71
  description: fallbackDescription,
71
72
  url: canonical,
72
73
  siteName,
@@ -74,7 +75,7 @@ export function buildCategoryMetadata({ category, canonical, useYoast = true, si
74
75
  },
75
76
  twitter: {
76
77
  card: "summary_large_image",
77
- title: category.name,
78
+ title: fallbackTitle,
78
79
  description: fallbackDescription,
79
80
  },
80
81
  };
@@ -82,7 +83,7 @@ export function buildCategoryMetadata({ category, canonical, useYoast = true, si
82
83
  return fallbackMetadata;
83
84
  const meta = extractYoastMetadata(category, {
84
85
  url: canonical,
85
- title: category.name,
86
+ title: fallbackTitle,
86
87
  description: fallbackDescription,
87
88
  siteName,
88
89
  }, { siteUrl, wpApiUrl });
@@ -3,7 +3,7 @@ import type { Metadata } from "next";
3
3
  type CategoryStaticParamsInput = {
4
4
  baseApiUrl: string;
5
5
  perPage: number;
6
- lang: string;
6
+ lang?: string;
7
7
  langId?: string;
8
8
  };
9
9
  type CategoryStaticParam = {
@@ -14,7 +14,7 @@ export declare function getCategoryPaginationStaticParams({ baseApiUrl, perPage,
14
14
  export declare function fetchCategoryBySlug({ baseApiUrl, slug, lang, }: {
15
15
  baseApiUrl: string;
16
16
  slug: string;
17
- lang: string;
17
+ lang?: string;
18
18
  }): Promise<WPCategory | null>;
19
19
  export declare function fetchCategoryPaginatedPosts({ baseApiUrl, categoryId, page, perPage, langId, }: {
20
20
  baseApiUrl: string;
@@ -1,15 +1,17 @@
1
- import { buildCategoryMetadata } from "./index";
2
- import { createWpClient } from "../../api/wpClient";
3
- import { getPostsTotalPages, listPostsWithEmbeds } from "../../api/posts";
4
- import { mapCategoryEntity } from "../../api/mappers";
5
- import { getCategoryBySlug as getCategoryBySlugApi } from "../../api/categories";
6
- import { ensureTrailingSlash } from "../../utils";
1
+ import { buildCategoryMetadata } from "./index.js";
2
+ import { createWpClient, isValidWpApiUrl } from "../../api/wpClient.js";
3
+ import { getPostsTotalPages, listPostsWithEmbeds } from "../../api/posts.js";
4
+ import { mapCategoryEntity } from "../../api/mappers.js";
5
+ import { getCategoryBySlug as getCategoryBySlugApi } from "../../api/categories.js";
6
+ import { ensureTrailingSlash } from "../../utils/index.js";
7
7
  export async function getCategoryPaginationStaticParams({ baseApiUrl, perPage, lang, langId, }) {
8
- const { categoriesApi } = createWpClient({
9
- baseUrl: baseApiUrl,
10
- });
8
+ if (!isValidWpApiUrl(baseApiUrl))
9
+ return [];
11
10
  try {
12
- const categories = await categoriesApi.listAll({ lang });
11
+ const { categoriesApi } = createWpClient({
12
+ baseUrl: baseApiUrl,
13
+ });
14
+ const categories = await categoriesApi.listAll(lang ? { lang } : undefined);
13
15
  const paths = [];
14
16
  for (const category of categories) {
15
17
  const totalPagesResult = await getPostsTotalPages({
@@ -1,4 +1,4 @@
1
- import { listPostsWithEmbeds } from "../../api/posts";
1
+ import { listPostsWithEmbeds } from "../../api/posts.js";
2
2
  export async function fetchHomePosts({ baseApiUrl, perPage, langId, }) {
3
3
  const postsResponse = await listPostsWithEmbeds({
4
4
  perPage,
@@ -1,6 +1,6 @@
1
- import { getPlainTextExcerpt, getOgImageUrl, getFeaturedMedia, } from "../../utils";
2
- import { withXDefault } from "../../utils/hreflang";
3
- import { listPostsWithEmbeds, getPostsTotalPages } from "../../api/posts";
1
+ import { getPlainTextExcerpt, getOgImageUrl, getFeaturedMedia, } from "../../utils/index.js";
2
+ import { withXDefault } from "../../utils/hreflang.js";
3
+ import { listPostsWithEmbeds, getPostsTotalPages } from "../../api/posts.js";
4
4
  export async function getHomePaginationStaticParams({ baseApiUrl, perPage, langId, }) {
5
5
  const totalPagesResult = await getPostsTotalPages({
6
6
  baseApiUrl,