Members
(constant) BUILD_TIME
The header clock, exercised the only way that can see the defect it had: server-render at one instant, hydrate at another.
render() from testing-library cannot catch this. It mounts on an empty container, so the text
it produces is the client's by construction and the test passes whatever the component does. The
pages here are statically generated, so the interesting gap is between the moment the HTML was
built and the moment a visitor loads it — days or weeks, not milliseconds.
The bug: the four fields carried suppressHydrationWarning, which tells React to keep the
server's text and skip the DOM patch. After that React only rewrites a node whose value differs
from the PREVIOUS CLIENT RENDER, and the weekday, day and month are stable between one render and
the next, so the build's date stayed on screen until midnight — days late on a static page. The
time recovered on its own because the 60 s interval changes it.
(constant) CONSENT_STORAGE_KEY
Where the visitor's answer is kept. _app reads the same key from its inline
bootstrap so a returning visitor's grant is replayed into Consent Mode before
gtag.js ever runs — otherwise every visit would start denied.
- Source:
(constant) DWELL_MS
And how long, so that a flick of the wheel to the bottom does not qualify.
- Source:
(constant) MIN_SCROLLABLE_PX
Below this, a scroller is a list or a sidebar rather than something that was read. Measured: the blog body is 2409px and the privacy policy 4692, against an 800px window.
- Source:
(constant) OG_IMAGE_WIDTH
SDD-L04: dimensions and alt added alongside the URL. Without og:image:width/height a scraper
has to fetch the image before it can lay out a card, and some (LinkedIn in particular) fall back to
a small preview or no image rather than wait. Every OG image this site references is 1200x630 —
og-home.jpg and all 15 post images in public/posts/ — so the values are accurate rather than
guessed. og:image:alt is the accessible description consumers read out.
- Source:
(constant) OG_LOCALES
Tag builders for
These are plain functions returning arrays of elements, deliberately NOT React
components. next/head only picks up tags that are direct children, inside a
single React.Fragment, or in an array — a custom component renders server-side
and then vanishes on client-side navigation (vercel/next.js#8384). Arrays are
documented as supported, so this split is safe where a
- Source:
(constant) READ_FRACTION
How far down the surface counts as having read it.
- Source:
(constant) STATUS_ICONS
SDD-L12-T8. One icon per status item, keyed by testId so a rename in site.ts fails loudly
here rather than silently rendering nothing. Kept in the component because site.ts is data —
importing React components into it would make every consumer of the constants pull in icons.
- Source:
(constant) UNTRANSLATED_PATH_PREFIXES
Paths whose content is identical in every locale, so no es/gl alternate may be claimed.
SDD-L08. data/legal/ holds three files with no locale suffix and legal/[slug].tsx never
receives the locale, so the same English document is served at /legal/*, /es/legal/* and
/gl/legal/* — while these tags told Google three translations existed. hreflang is a
reciprocal claim about translated content; pointing it at the same English text is telling
search engines something untrue, and it invites them to serve a Spanish-speaking reader an
English page they cannot use.
This is a stopgap, not the fix. The fix is L08-T2 — actual es/gl legal documents — which is
owner work: these texts invoke Spanish law (Ley 34/2002, LOPD-GDD, RD 1720/2007) and make
representations to users about data handling. A machine translation is not adequate. Until then,
claiming nothing beats claiming falsely.
- Source:
(constant) alternateLinks
Translated URLs for the current blog post.
- Source:
(constant) articleTags
article:* metadata, emitted for blog posts only.
- Source:
(constant) blogTags
Structured data and hreflang links for a blog post.
- Source:
(constant) consentSignals
The four Consent Mode v2 signals this site can move. Storage that
is not tied to identifying the visitor (functionality_storage,
security_storage) is granted by default in _app and never asked about,
because it is exempt from prior consent.
- Source:
hydrated
Held so afterEach can unmount it: the clock's interval keeps the process alive otherwise.
(constant) ogLocaleTags
og:locale alone tells a scraper which language THIS page is; the alternates declare that the other translations exist. hreflang covers this for search engines, but Open Graph consumers (LinkedIn, Facebook, Slack) do not read it.
- Source:
(constant) resolveSeoUrls
Resolve the canonical URL and the derived values every tag group needs.
- Source:
(constant) staticHreflangTags
hreflang links for the static, non-blog pages.
- Source:
ArticlePanel(readTime) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
readTime |
string | The read time of the article |
- Source:
Returns:
- Type
- JSX.Element
Example
<ArticlePanel readTime="2" />;
AsidePanel() → {JSX.Element}
- Source:
Returns:
- Type
- JSX.Element
Example
<AsidePanel />;
Avatar(name, description, img, alt) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
name |
string | Name of the user |
description |
string | Description of the user |
img |
string | Image of the user |
alt |
string | Alt text for the image |
- Source:
Returns:
- Avatar component
- Type
- JSX.Element
Example
<Avatar name="John Doe" description="Lorem ipsum" img="/images/avatar.jpg" alt="John Doe" />;
BackgroundImage() → {JSX.Element}
- Source:
Returns:
- Type
- JSX.Element
Example
<BackgroundImage />;
CodeFirstPaint(props) → {JSX.Element}
CH.Code with the active file painted in the server HTML.
Code Hike lays code out from measurements it can only take in the browser, so until the page
hydrates it renders the lines at opacity: 0 and the window is an empty dark box. On a cold load
that is the whole JS download. This paints the same highlighted tokens, which the MDX compiler
already passes in files, over that box until Code Hike has measured. Both happen in layout
effects of the same commit, so the swap never reaches the screen as an empty frame.
aria-hidden: the text is also in Code Hike's own hidden layer, and a reader must not hear it twice.
Parameters:
| Name | Type | Description |
|---|---|---|
props |
Props | The props |
- Source:
Returns:
- Type
- JSX.Element
ControlButtons(disabled, withPadding, onClickClose, onClickMinimise, onClickMaximise) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
disabled |
boolean | If true, the maximise button will be disabled |
withPadding |
boolean | If true, the container will have padding |
onClickClose |
function | Callback function when close button is clicked |
onClickMinimise |
function | Callback function when minimise button is clicked |
onClickMaximise |
function | Callback function when maximise button is clicked |
- Source:
Returns:
- Type
- JSX.Element
Example
<ControlButtons />;
CookieConsent() → {JSX.Element|null}
Prior-consent gate for analytics storage. Until the visitor
answers, _app has already told gtag every identifying signal is denied, so
nothing is written; this component only ever moves that state.
- Source:
Returns:
- Type
- JSX.Element | null
Example
<CookieConsent />;
CountDown(date, caption) → {JSX.Element}
Countdown to date provided in months, days, hours, minutes and seconds
Parameters:
| Name | Type | Description |
|---|---|---|
date |
string | date to countdown to |
caption |
string | caption to display |
- Source:
Returns:
- Type
- JSX.Element
CryptoPrice() → {JSX.Element}
Show the current price of XRP in EUR
- Source:
Returns:
- Type
- JSX.Element
Example
<CryptoPrice />
DateAndHour(children, minutes) → {JSX.Element}
This component is a Clock and a Weather Widget
Parameters:
| Name | Type | Description |
|---|---|---|
children |
ReactNode | The children |
minutes |
number | The number of minutes to update the clock |
- Source:
Returns:
- Type
- JSX.Element
DeploymentStatus() → {JSX.Element}
- Shows the current deployment status
- Source:
Returns:
- DeploymentStatus component
- Type
- JSX.Element
Example
- <DeploymentStatus />
Dialog(dialogRef, open, withPadding, modalMode, header, body, footer, large, fitContent, label, onClose) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
dialogRef |
React.RefObject.<HTMLDivElement> | Ref to the dialog element |
open |
boolean | If true, the dialog will be open |
withPadding |
boolean | If true, the dialog will have padding |
modalMode |
boolean | If true, the dialog will be not complete screen |
header |
ReactNode | The header of the dialog |
body |
ReactNode | The body of the dialog |
footer |
ReactNode | The footer of the dialog |
large |
boolean | If true, the dialog will be large |
fitContent |
boolean | If true, a small-screen modal sizes to its content |
label |
string | Accessible name for the window |
onClose |
function | Invoked when Escape is pressed |
- Source:
Returns:
- Type
- JSX.Element
Example
<Dialog
dialogRef={dialogRef}
open={open}
withPadding={withPadding}
modalMode={modalMode}
header={<h1>Header</h1>}
body={<p>Body</p>}
footer={<button>Footer</button>}
large={large}
/>;
Dock()
This component is the dock that appears on the bottom of the screen
- Source:
Returns:
JSX.Element
Folder()
A Dock folder: one slot that opens a small panel above the Dock listing the tools.
It is not a route. /next-leak and /next-coverage stay the canonical entries, so the folder
must not take a URL of its own or appear in the sitemap.
The panel is always in the DOM and only hidden when closed, so both anchors are in the server
HTML. That is how /next-leak is reachable from the home page today, and a panel that only
existed after hydration would take that away from the new page.
- Source:
FormattedDate(string) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
string |
Date |
- Source:
Returns:
- Type
- JSX.Element
Example
<Date date="01-02-2023" />
GithubStars()
A menu bar extra for the repository, of the kind macOS puts to the left of the clock: the count is always on the bar, and clicking it opens a panel that reports the rest and offers the one action worth offering.
It deliberately does not link anywhere on the bar itself. The count is a status, and a status
that navigates away on a stray click is a trap — the <a> lives inside the panel, where the
reader has already said they are interested.
- Source:
GoogleAdsense(client, slot) → {JSX.Element}
- The adsense will be rendered only in production mode and horizontal only on mobile
Parameters:
| Name | Type | Description |
|---|---|---|
client |
string | The client id |
slot |
string | The slot id |
- Source:
Returns:
- Type
- JSX.Element
Example
<GoogleAdsense slot="1234567890" />;
GridLayoutControl(routeName) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
routeName |
string | The name of the route |
- Source:
Returns:
- Type
- JSX.Element
Example
<GridLayoutControl routeName="System Preferences" />;
Header(children) → {JSX.Element}
Header navigation bar component with the following features:
- Bitcoin logo
- Current route
- Social links
- XRP price
- Indexed pages counter
- Total views counter
- Heating temperature
- Date and hour
- Weather Widget
Parameters:
| Name | Type | Description |
|---|---|---|
children |
ReactNode | The children to display: ; |
- Source:
Returns:
- Type
- JSX.Element
Heating() → {JSX.Element}
- Shows the current temperature of the house and the outside temperature
- Source:
Returns:
- News component
- Type
- JSX.Element
Icon(src, alt, testId) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
src |
string | Image source |
alt |
string | Image alt text |
testId |
string | Test id for testing |
- Source:
Returns:
- Type
- JSX.Element
Example
<Icon src="/images/nextjs.svg" alt="NextJS" testId="nextjs-icon" />
IconWithName(icon, alt, name, horizontal, onClick, pressed) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
icon |
string | The path to the icon |
alt |
string | The alt text for the icon |
name |
string | Text to display |
horizontal |
boolean | If true, the icon will be displayed horizontally |
onClick |
function | Makes this a button; omit it for a static tile |
pressed |
boolean | Toggle state, forwarded as aria-pressed |
- Source:
Returns:
- Type
- JSX.Element
Example
<IconWithName icon="/images/icons/terminal.svg" alt="Terminal" name="Terminal" />;
<IconWithName icon="/images/icons/terminal.svg" alt="Terminal" name="Terminal" horizontal />;
IndexedCounter() → {JSX.Element}
This component is used to display the number of indexed pages on Google
- Source:
Returns:
- Type
- JSX.Element
Example
<IndexedCounter />
LangSelect() → {JSX.Element}
- Source:
Returns:
- Type
- JSX.Element
Example
<LangSelect />;
Layout(className, children) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
className |
string | The class name for the different pages |
children |
JSX.Element | Content to be rendered |
- Source:
Returns:
- Type
- JSX.Element
Example
<Layout meta={meta} isBlog={true}>
<div>Content</div>
</Layout>;
NavLinks() → {JSX.Element}
The profile and artifact links, as icons in the left zone beside the identity.
SDD-L12-T9. T8 put them in the right zone, which read as a second cluster of status items hanging off the countdown. They are navigation, not status — on a macOS menu bar that belongs next to the app identity — and that is also where they were before T8 moved them.
- Source:
Returns:
- Type
- JSX.Element
NavList(title, list, category, isCategory) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
title |
string | The title of the list |
list |
Array.<object> | The list of items |
category |
string | The category has other styles |
isCategory |
boolean | If is category the icon will be a folder |
- Source:
Returns:
- Type
- JSX.Element
Example
<NavList title="Categories" list={categories} category={category} isCategory={true} />;
NavigationArrows(hidden, disabledLeft, disabledRight, onClickLeft, onClickRight) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
hidden |
boolean | If true, the arrows will be hidden |
disabledLeft |
boolean | If true, the left arrow will be disabled |
disabledRight |
boolean | If true, the right arrow will be disabled |
onClickLeft |
function | Callback function when left arrow is clicked |
onClickRight |
function | Callback function when right arrow is clicked |
- Source:
Returns:
- Type
- JSX.Element
Example
<NavigationArrows />;
News(string) → {JSX.Element}
- Show the latest news about the city
Parameters:
| Name | Type | Description |
|---|---|---|
string |
city | City name |
- Source:
Returns:
- News component
- Type
- JSX.Element
Notification(title, message, type) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
title |
string | The title of the notification |
message |
string | The message of the notification |
type |
string | The type of the notification |
- Source:
Returns:
- Type
- JSX.Element
Example
<Notification title="Success" message="This is a success message" type="success" />;
<Notification title="Error" message="This is an error message" type="error" />;
PostList(posts, slug, category) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
posts |
Array.<object> | The list of posts |
slug |
string | The slug is used to highlight the selected post |
category |
string | The taxonomy segment being browsed, preserved in every link |
- Source:
Returns:
- Type
- JSX.Element
Example
<PostList posts={posts} slug={slug} category={category} />;
RenderManager(Error, boolean, string, string) → {React.ReactNode}
- Renders children or error/loading icon
SDD-L07: error was typed boolean. It never was one — every caller passes SWR's error, an
Error object, and it typechecked only because useSWR<Data> leaves the error generic at any.
The declaration therefore discarded the only information the widget had about what went wrong,
which is why all ten of them showed the same icon and the same sentence whether the route was
down, the network was gone, or the response no longer matched its contract.
errorTitle still wins when a caller supplies one, so widget-specific wording ("heating
unavailable") is unchanged; the distinction only fills in where there was no wording at all.
Parameters:
| Name | Type | Description |
|---|---|---|
Error |
error | | undefined - the failure, if there was one |
boolean |
loading | loading state |
string |
errorTitle | Title for error icon |
string |
loadingTitle | Title for loading icon |
React.ReactNode |
children | Children to render |
- Source:
Returns:
- Returns children or error/loading icon
- Type
- React.ReactNode
Route() → {JSX.Element}
Translate the route to the current language
- Source:
Returns:
- Type
- JSX.Element
SEO(meta, isBlog, noimage) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
meta |
object | The object containing the meta data for SEO |
isBlog |
boolean | Whether the page is a blog post the SEO changes |
noimage |
boolean | Whether to show the image in the SEO |
- Source:
Returns:
- Type
- JSX.Element
Example
<SEO meta={meta} isBlog={true} />;
SearchInput(value, disabled, onBlur, onChange, placeHolderText, label) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
value |
string | The value of the input |
disabled |
boolean | If true, the input will be disabled |
onBlur |
function | Callback function when input is blurred |
onChange |
function | Callback function when input is changed |
placeHolderText |
string | The placeholder text for the input |
label |
string | Accessible name for the field |
- Source:
Returns:
- Type
- JSX.Element
Example
<SearchInput />;
SidesShift(handleClick, leftPosition, label, expanded) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
handleClick |
function | Makes this a real button; omit it for a decorative grabber |
leftPosition |
boolean | If true, the button will be positioned on the left |
label |
string | Accessible name, required alongside handleClick |
expanded |
boolean | Panel open state, forwarded as aria-expanded |
- Source:
Returns:
- Type
- JSX.Element
Example
<SidesShift />;
<SidesShift handleClick={toggle} label="Show posts" expanded={open} />;
StarCounter() → {JSX.Element}
Component that shows the number of stars of the repository
- Source:
Returns:
Component
- Type
- JSX.Element
StarPrompt()
A macOS notification asking for a star, shown once per browser and never on arrival.
The menu bar extra beside the clock is the permanent, passive half of this: it is there for anyone who looks. This is the other half — it asks, once, and only of someone who has actually read something. Both conditions have to hold: 60% of the surface AND 30 seconds on it, because either one alone is satisfied by a wheel flick or by an abandoned tab.
The window never scrolls here. This is a desktop simulation: document.documentElement is
exactly the viewport on every route, and the reading happens inside a panel — the blog body and
the legal article are the two that scroll, at 2409px and 4692px against an 800px window. A
listener on window would therefore never have fired once, which is how this was written first
and what measuring it caught. So the listener is on document in the CAPTURE phase, because a
scroll event on an element does not bubble, and the fraction is read off whatever scrolled.
The key is written the moment it appears, not when it is answered. Ignoring a request is an answer, and a prompt that returns until it gets a click is the pattern this is trying not to be.
- Source:
TimeRead(readTime) → {JSX.Element}
Parameters:
| Name | Type | Description |
|---|---|---|
readTime |
string | The read time of the article |
- Source:
Returns:
- Type
- JSX.Element
Example
<TimeRead readTime="2" />;
VercelAnalytics()
measure-real-traffic: cookieless visit counting (Vercel Web Analytics). Visitors are told apart by a hash of the request discarded after 24 hours, not by a cookie, so it counts visitors who reject or ignore the consent banner, which GA4 under Consent Mode v2 cannot. Skipped for Lighthouse runs, like gtag, so audits measure the site and not the tracker.
- Source:
ViewCounter(all) → {JSX.Element}
Component that shows the number of views of the current page or the totally of website, include de new users
Parameters:
| Name | Type | Description |
|---|---|---|
all |
If true, shows the total of views and new users |
- Source:
Returns:
- Type
- JSX.Element
VisibilityManager(hideOnDesktop, hideOnTablet, hideOnMobile) → {ReactElement|null}
Parameters:
| Name | Type | Description |
|---|---|---|
hideOnDesktop |
boolean | If true, the component will be hidden on desktop |
hideOnTablet |
boolean | If true, the component will be hidden on tablet |
hideOnMobile |
boolean | If true, the component will be hidden on mobile |
- Source:
Returns:
- Type
- ReactElement | null
Example
<VisibilityManager hideOnDesktop={true}>
Weather(cities, open, handleClose) → {JSX.Element}
- Show the weather of array of cities and the last news of each city
Parameters:
| Name | Type | Description |
|---|---|---|
cities |
Array.<string> | Cities to get weather |
open |
boolean | Open or close the component |
handleClose |
function | Function to close the component |
- Source:
Returns:
- News component
- Type
- JSX.Element
calculateTimeLeft(date, setTime, timeLeft) → {TimeLeft}
This function is used to calculate the time left until the date
Parameters:
| Name | Type | Description |
|---|---|---|
date |
string | The date to calculate the time left |
setTime |
function | The function to set the time left |
timeLeft |
TimeLeft | The time left object |
- Source:
Returns:
- The time left object
- Type
- TimeLeft
Example
calculateTimeLeft('2021-12-31', setTime)
clockFields()
The four spans the clock renders, in order: weekday, day, month, time.
errorMessageId()
- Decide which of the failure message ids fits this error.
Kept outside the component so the mapping can be read on its own: a route that answered a status we do not accept is a different event from a route that answered a body we do not recognise, and a reader can act on the difference.
- Source:
panel()
The reading surface is a panel, not the window — see the component. jsdom reports 0 for every layout box, so the geometry has to be stated: these numbers stand in for the blog body, measured at 2409px of content in an 800px window.
sincePush()
How long ago the last push was, in the coarsest unit that is still true. react-intl does the
wording per locale, so this never assembles a sentence by hand. Its formatter rather than a bare
Intl.RelativeTimeFormat, which has no Galician data in Chromium (src/intl/galician.ts).
- Source:
useDeploymentStatus() → {Object}
- Fetches the deployment status
- Source:
Returns:
- Type
- Object
Example
- const { data, isLoading, error } = useDeploymentStatus();
usePostComponents(meta) → {object}
MDX component overrides that inject the E-E-A-T byline under the post title.
Every MDX body opens with # Title immediately followed by
Parameters:
| Name | Type | Description |
|---|---|---|
meta |
object | Post frontmatter carrying the author and publication date. |
- Source:
Returns:
The MDX components map to hand to
- Type
- object