příklady / weby

Osobní web

Jednostránkový web vykreslený na serveru, se stylem servírovaným z public/.

denohtmlstatic

Tato služba vykreslí jednu HTML stránku z jediného handleru a svůj styl servíruje jako statický soubor. Cokoli ve složce public/ platforma publikuje na vlastní cestě, takže public/style.css je dostupný na /style.css bez jediného řádku kódu. Každý další požadavek zpracuje handler.

Protože se stránka skládá na serveru, můžete do ní vložit živá data: tento příklad vepíše do patičky aktuální rok. Upravte objekt profilu na začátku main.ts, aby web patřil vám, a nasaďte. Žádný build, žádný framework.

kód
main.ts
// A personal website rendered on the server. This one handler builds the whole
// page as HTML; the stylesheet lives in public/ and is served for you by the
// platform, so the service never touches static files itself.
//
// Anything under public/ is published at its own path (public/style.css is
// served at /style.css). Every other request runs this handler, which is why
// we can inject live data like the current year.

const PROFILE = {
  name: "Ada Lovelace",
  role: "Mathematician and writer",
  bio: "I work on analytical engines and believe a machine can do more than crunch numbers. This site is a single Frontback service: one file that renders itself.",
  links: [
    { label: "Email", href: "mailto:ada@example.com" },
    { label: "GitHub", href: "https://github.com/" },
    { label: "Notes", href: "https://example.com/notes" },
  ],
};

export default async (req: Request): Promise<Response> => {
  const { pathname } = new URL(req.url);

  // Only "/" is dynamic. /style.css and any other public asset are served
  // by the platform and never reach this handler.
  if (pathname !== "/") {
    return new Response("Not found\n", { status: 404 });
  }

  return new Response(renderPage(), {
    headers: { "content-type": "text/html; charset=utf-8" },
  });
};

function renderPage(): string {
  const year = new Date().getFullYear();
  const links = PROFILE.links
    .map((link) => `<a href="${link.href}">${link.label}</a>`)
    .join("\n        ");

  return `<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>${PROFILE.name}</title>
    <link rel="stylesheet" href="/style.css" />
  </head>
  <body>
    <main>
      <h1>${PROFILE.name}</h1>
      <p class="role">${PROFILE.role}</p>
      <p class="bio">${PROFILE.bio}</p>
      <nav>
        ${links}
      </nav>
    </main>
    <footer>&copy; ${year} ${PROFILE.name}</footer>
  </body>
</html>
`;
}
public/style.css
:root {
  color-scheme: light dark;
  --fg: #16181d;
  --muted: #5c636e;
  --bg: #ffffff;
  --line: #e6e8ec;
}

@media (prefers-color-scheme: dark) {
  :root {
    --fg: #e9ebf0;
    --muted: #9aa1ad;
    --bg: #101216;
    --line: #262a31;
  }
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2rem;
  padding: 4rem 1.5rem;
  background: var(--bg);
  color: var(--fg);
  font: 17px/1.6 system-ui, -apple-system, "Segoe UI", sans-serif;
}

main {
  max-width: 34rem;
  margin: 0 auto;
}

h1 {
  margin: 0 0 0.25rem;
  font-size: 2.25rem;
  letter-spacing: -0.02em;
}

.role {
  margin: 0 0 1.5rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.8rem;
}

.bio {
  margin: 0 0 2rem;
}

nav {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
}

nav a {
  color: var(--fg);
  text-decoration: none;
  border-bottom: 1px solid var(--line);
  padding-bottom: 2px;
}

nav a:hover {
  border-color: var(--fg);
}

footer {
  max-width: 34rem;
  margin: 0 auto;
  color: var(--muted);
  font-size: 0.85rem;
}

Go beyond what seems possible.