mirror of
https://github.com/Xevion/rdap.git
synced 2025-12-06 15:16:07 -06:00
build: upgrade Tailwind CSS from v3 to v4
- Migrate from Tailwind CSS 3.x to 4.1.15 - Replace autoprefixer with @tailwindcss/postcss 4.1.15 - Update PostCSS configuration for Tailwind v4 compatibility - Convert globals.scss to globals.css for CSS-first configuration - Remove legacy tailwind.config.cjs in favor of CSS-based config - Update all component imports to use new globals.css - Remove old.tsx.disabled file
This commit is contained in:
@@ -32,6 +32,7 @@
|
||||
"zod": "^4.1.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.15",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@types/node": "^24.9.1",
|
||||
@@ -40,14 +41,13 @@
|
||||
"@typescript-eslint/eslint-plugin": "^8.46.2",
|
||||
"@typescript-eslint/parser": "^8.46.2",
|
||||
"@vitest/ui": "^3.2.4",
|
||||
"autoprefixer": "^10.4.7",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint-config-next": "15.5.6",
|
||||
"happy-dom": "^20.0.8",
|
||||
"postcss": "^8.4.14",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-tailwindcss": "^0.7.1",
|
||||
"tailwindcss": "^3.2.0",
|
||||
"tailwindcss": "^4.1.15",
|
||||
"type-fest": "^5.1.0",
|
||||
"typescript": "^5.9.3",
|
||||
"vitest": "^3.2.4"
|
||||
|
||||
958
pnpm-lock.yaml
generated
958
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
'@tailwindcss/postcss': {},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ const AbstractCard: FunctionComponent<AbstractCardProps> = ({
|
||||
<div className="mb-4 overflow-clip rounded bg-zinc-800 shadow">
|
||||
{header != undefined || data != undefined ? (
|
||||
<div className="flex bg-zinc-700 p-2 pl-3 md:pl-5">
|
||||
<div className="grow space-x-2">{header}</div>
|
||||
<div className="flex grow gap-2">{header}</div>
|
||||
{url != undefined ? (
|
||||
<div className="pr-2">
|
||||
<a href={url} target="_blank" rel="noreferrer">
|
||||
@@ -96,7 +96,7 @@ const AbstractCard: FunctionComponent<AbstractCardProps> = ({
|
||||
)}
|
||||
</div>
|
||||
{footer != null ? (
|
||||
<div className="space-x-2 bg-zinc-700 p-2 pl-5">{footer}</div>
|
||||
<div className="flex gap-2 bg-zinc-700 p-2 pl-5">{footer}</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -23,7 +23,7 @@ const ErrorCard: FunctionComponent<ErrorCardProps> = ({
|
||||
)}
|
||||
>
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="shrink-0">
|
||||
<XCircleIcon className="h-5 w-5 text-red-300" aria-hidden="true" />
|
||||
</div>
|
||||
<div className="ml-3 w-full text-sm text-red-300">
|
||||
@@ -35,7 +35,7 @@ const ErrorCard: FunctionComponent<ErrorCardProps> = ({
|
||||
) : null}
|
||||
<div className="mt-2">
|
||||
{issues != undefined ? (
|
||||
<ul role="list" className="list-disc space-y-1 pl-5">
|
||||
<ul role="list" className="flex list-disc flex-col gap-1 pl-5">
|
||||
{issues.map((issueText, index) => (
|
||||
<li key={index}>{issueText}</li>
|
||||
))}
|
||||
|
||||
@@ -146,7 +146,7 @@ const LookupInput: FunctionComponent<LookupInputProps> = ({
|
||||
className={clsx(
|
||||
"lg:py-4.5 block w-full rounded-l-md border border-transparent",
|
||||
"bg-zinc-700 py-2 pl-10 pr-1.5 text-sm placeholder-zinc-400 placeholder:translate-y-2 focus:text-zinc-200",
|
||||
" focus:outline-none sm:text-sm md:py-3 md:text-base lg:text-lg"
|
||||
" focus:outline-hidden sm:text-sm md:py-3 md:text-base lg:text-lg"
|
||||
)}
|
||||
disabled={isLoading}
|
||||
placeholder={placeholders[selected]}
|
||||
@@ -184,8 +184,8 @@ const LookupInput: FunctionComponent<LookupInputProps> = ({
|
||||
<ListboxButton
|
||||
className={clsx(
|
||||
"relative h-full w-full cursor-default whitespace-nowrap rounded-r-lg bg-zinc-700 py-2 pl-1 pr-10 text-right",
|
||||
"text-xs focus:outline-none focus-visible:border-indigo-500 sm:text-sm md:text-base lg:text-lg",
|
||||
"focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 "
|
||||
"text-xs focus:outline-hidden focus-visible:border-indigo-500 sm:text-sm md:text-base lg:text-lg",
|
||||
"focus-visible:ring-2 focus-visible:ring-white/75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 "
|
||||
)}
|
||||
>
|
||||
{/* Fetch special text for 'auto' mode, otherwise just use the options. */}
|
||||
@@ -229,7 +229,7 @@ const LookupInput: FunctionComponent<LookupInputProps> = ({
|
||||
<ListboxOptions
|
||||
className={clsx(
|
||||
"scrollbar-thin absolute right-0 mt-1 max-h-60 min-w-full overflow-auto rounded-md bg-zinc-700 py-1",
|
||||
"text-zinc-200 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"
|
||||
"text-zinc-200 shadow-lg ring-1 ring-black/5 focus:outline-hidden sm:text-sm"
|
||||
)}
|
||||
>
|
||||
{Object.entries(objectNames).map(([key, value]) => (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type AppType } from "next/dist/shared/lib/utils";
|
||||
|
||||
import "../styles/globals.scss";
|
||||
import "../styles/globals.css";
|
||||
|
||||
const MyApp: AppType = ({ Component, pageProps }) => {
|
||||
return <Component {...pageProps} />;
|
||||
|
||||
@@ -41,7 +41,7 @@ const Index: NextPage = () => {
|
||||
content="xevion, rdap, whois, rdap, domain name, dns, ip address"
|
||||
/>
|
||||
</Head>
|
||||
<nav className="bg-zinc-850 px-5 py-4 shadow-sm">
|
||||
<nav className="bg-zinc-850 px-5 py-4 shadow-xs">
|
||||
<span
|
||||
className="text-xl font-medium text-white"
|
||||
style={{ fontSize: "larger" }}
|
||||
|
||||
@@ -1,425 +0,0 @@
|
||||
import { type NextPage } from "next";
|
||||
import Head from "next/head";
|
||||
import type { Register, TargetType } from "@/types";
|
||||
import { placeholders, registryURLs } from "@/constants";
|
||||
import { domainMatch, getBestURL, getType } from "@/rdap";
|
||||
import type { FormEvent } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { truthy } from "@/helpers";
|
||||
import Generic, { type ParsedGeneric } from "@/components/lookup/Generic";
|
||||
import type { ZodSchema } from "zod";
|
||||
import { DomainSchema, RegisterSchema } from "@/schema";
|
||||
|
||||
const Old: NextPage = () => {
|
||||
const [requestJSContact, setRequestJSContact] = useState(false);
|
||||
const [followReferral, setFollowReferral] = useState(false);
|
||||
const [object, setObject] = useState<string>("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [response, setResponse] = useState<ParsedGeneric | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [registryData, setRegistryData] = useState<Record<
|
||||
string,
|
||||
Register
|
||||
> | null>(null);
|
||||
|
||||
// Change the selected type automatically
|
||||
const uriType = useMemo<TargetType>(
|
||||
function () {
|
||||
return getType(object) ?? "domain";
|
||||
},
|
||||
[object]
|
||||
);
|
||||
|
||||
async function loadRegistryData() {
|
||||
setLoading(true);
|
||||
|
||||
let registersLoaded = 0;
|
||||
const totalRegisters = Object.keys(registryURLs).length;
|
||||
const responses = await Promise.all(
|
||||
Object.entries(registryURLs).map(async ([registryType, url]) => {
|
||||
const response = await fetch(url);
|
||||
registersLoaded++;
|
||||
return {
|
||||
registryType,
|
||||
response: RegisterSchema.parse(await response.json()),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
setRegistryData(() => {
|
||||
return Object.fromEntries(
|
||||
responses.map(({ registryType, response }) => [registryType, response])
|
||||
) as Record<string, Register>;
|
||||
});
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
// construct an RDAP URL for the given object
|
||||
function getRDAPURL(object: string): string | null {
|
||||
let urls: string[] = [];
|
||||
|
||||
if (registryData == null) {
|
||||
console.log("Registry data not loaded.");
|
||||
return null;
|
||||
}
|
||||
const service = registryData[uriType]?.services;
|
||||
if (service == undefined) return null;
|
||||
|
||||
services: for (const serviceItem of service) {
|
||||
// special case for object tags, since the registrant email address is in the 0th position
|
||||
const [rangeIndex, urlIndex] = uriType == "entity" ? [1, 2] : [0, 1];
|
||||
|
||||
for (const tld of serviceItem[rangeIndex]!) {
|
||||
let match = false;
|
||||
|
||||
switch (uriType) {
|
||||
case "domain":
|
||||
match = domainMatch(tld, object);
|
||||
break;
|
||||
// case "autnum":
|
||||
// match = asnMatch(range, object);
|
||||
// break;
|
||||
// case "entity":
|
||||
// match = entityMatch(range, object);
|
||||
// break;
|
||||
// case "ip":
|
||||
// match = ipMatch(range, object);
|
||||
// break;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
urls = serviceItem[urlIndex]!;
|
||||
break services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no match
|
||||
if (urls.length == 0) return null;
|
||||
|
||||
let url = getBestURL(urls);
|
||||
// some bootstrap entries have a trailing slash, some don't
|
||||
if (!url.endsWith("/")) url += "/";
|
||||
return `${url + uriType}/${object}`;
|
||||
}
|
||||
|
||||
async function submit(e?: FormEvent) {
|
||||
e?.preventDefault();
|
||||
|
||||
console.log(`Submit invoked. ${uriType}/${JSON.stringify(object)}`);
|
||||
const queryParams = requestJSContact ? "?jscard=1" : "";
|
||||
const [url, schema]: [string, ZodSchema<ParsedGeneric>] | [null, null] =
|
||||
(function () {
|
||||
switch (uriType) {
|
||||
// case 'url':
|
||||
// return [object];
|
||||
// case 'tld':
|
||||
// return `https://root.rdap.org/domain/${object}${queryParams}`;
|
||||
// case 'registrar':
|
||||
// return `https://registrars.rdap.org/entity/${object}-IANA${queryParams}`;
|
||||
// case 'json':
|
||||
// return `json://${object}`
|
||||
case "domain":
|
||||
const temp = getRDAPURL(object);
|
||||
if (temp) return [`${temp}${queryParams}`, DomainSchema];
|
||||
return [null, null];
|
||||
default:
|
||||
setError(`No RDAP URL available for ${uriType} ${object}.`);
|
||||
return [null, null];
|
||||
}
|
||||
})();
|
||||
|
||||
console.log(`URL: ${url ?? "null"}`);
|
||||
if (url != null) await sendQuery(url, schema, followReferral);
|
||||
}
|
||||
|
||||
async function sendQuery(
|
||||
url: string,
|
||||
schema: ZodSchema<ParsedGeneric>,
|
||||
followReferral = false
|
||||
) {
|
||||
setLoading(true);
|
||||
|
||||
let data: ParsedGeneric | null = null;
|
||||
if (url.startsWith("json://")) {
|
||||
console.log("Mock JSON query detected.");
|
||||
// run the callback with a mock XHR
|
||||
data = schema.parse(JSON.parse(url.substring(7)));
|
||||
} else {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
if (response.status == 404) setError("This object does not exist.");
|
||||
else if (response.status != 200)
|
||||
setError(`Error ${response.status}: ${response.statusText}`);
|
||||
data = schema.parse(await response.json());
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
setLoading(false);
|
||||
if (e instanceof Error) setError(e.toString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if (followReferral && data.hasOwnProperty('links') != undefined) {
|
||||
// console.log('Using followReferral.')
|
||||
// for (const link of data.links) {
|
||||
// if ('related' == link.rel && 'application/rdap+json' == link.type && link.href.match(/^(https?:|)\/\//i)) {
|
||||
// await sendQuery(link.href, false)
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
setLoading(false);
|
||||
console.log(data);
|
||||
try {
|
||||
setResponse(data);
|
||||
const url = `${window.location.origin}?type=${encodeURIComponent(
|
||||
uriType
|
||||
)}&object=${object}&request-jscontact=${
|
||||
requestJSContact ? 1 : 0
|
||||
}&follow-referral=${followReferral ? 1 : 0}`;
|
||||
window.history.pushState(null, document.title, url);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) setError(`Exception: ${e.message}`);
|
||||
else setError("Unknown error.");
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Load parameters from URL query string on page load
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
// if (params.has('type'))
|
||||
// setUriType(params.get('type') as ObjectType);
|
||||
|
||||
if (params.has("object")) setObject(params.get("object")!);
|
||||
|
||||
if (
|
||||
params.has("request-jscontact") &&
|
||||
truthy(params.get("request-jscontact"))
|
||||
)
|
||||
setRequestJSContact(true);
|
||||
|
||||
if (params.has("follow-referral") && truthy(params.get("follow-referral")))
|
||||
setFollowReferral(true);
|
||||
|
||||
loadRegistryData().catch(console.error);
|
||||
if (params.has("object") && (params.get("object")?.length ?? 0) > 0) {
|
||||
setObject(params.get("object")!);
|
||||
// submit().catch(console.error);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>rdap.xevion.dev</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="shortcut icon" href="/shortcut-icon.svg" />
|
||||
<meta name="description" content="" />
|
||||
<meta
|
||||
name="keywords"
|
||||
content="xevion, rdap, whois, rdap, domain name, dns, ip address"
|
||||
/>
|
||||
</Head>
|
||||
<>
|
||||
<style jsx>{`
|
||||
dd {
|
||||
margin: 0.5em 0 1em 2em;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.rdap-status-code,
|
||||
.rdap-event-time {
|
||||
border-bottom: 1px dashed silver;
|
||||
}
|
||||
|
||||
#object {
|
||||
text-transform: lowercase;
|
||||
}
|
||||
|
||||
#spinner-msg {
|
||||
height: 2em;
|
||||
display: inline-block;
|
||||
margin: -0.25em 0 0 0;
|
||||
padding: 0.25em 0 0 0;
|
||||
}
|
||||
`}</style>
|
||||
<nav className="navbar navbar-expand-lg navbar-dark shadow-sm">
|
||||
<span className="text-white" style={{ fontSize: "larger" }}>
|
||||
<a className="navbar-brand" href="#">
|
||||
rdap.xevion.dev
|
||||
</a>
|
||||
</span>
|
||||
</nav>
|
||||
<div className="container mx-auto max-w-screen-lg py-12">
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
void submit(e);
|
||||
}}
|
||||
className="form-inline"
|
||||
>
|
||||
<div className="col p-0">
|
||||
<div className="input-group">
|
||||
<div className="input-group-prepend">
|
||||
<select
|
||||
onChange={() => {
|
||||
return false;
|
||||
}}
|
||||
className="custom-select border-zinc-700 bg-zinc-800 text-zinc-200"
|
||||
id="type"
|
||||
name="type"
|
||||
value={uriType}
|
||||
>
|
||||
<option value="domain">Domain</option>
|
||||
<option value="tld">TLD</option>
|
||||
<option value="ip">IP/CIDR</option>
|
||||
<option value="autnum">AS Number</option>
|
||||
<option value="entity">Entity</option>
|
||||
<option value="registrar">Registrar</option>
|
||||
<option value="url">URL</option>
|
||||
<option value="json">JSON</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<input
|
||||
className="form-control border-zinc-700 bg-zinc-800 text-zinc-200 focus:border-zinc-600 focus:bg-zinc-700"
|
||||
type="text"
|
||||
placeholder={placeholders[uriType]}
|
||||
disabled={loading}
|
||||
onChange={(e) => {
|
||||
setObject(e.target.value);
|
||||
}}
|
||||
required
|
||||
/>
|
||||
|
||||
<div className="input-group-append">
|
||||
<input
|
||||
id="button"
|
||||
type="button"
|
||||
value="Submit"
|
||||
onClick={(event) => {
|
||||
void submit(event);
|
||||
}}
|
||||
className="btn btn-primary"
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div
|
||||
className="container p-0 italic text-[#aaa]"
|
||||
style={{ fontSize: "small" }}
|
||||
>
|
||||
<div className="col pt-3 pb-1">
|
||||
Options:
|
||||
<label htmlFor="request-jscontact">
|
||||
<input
|
||||
name="request-jscontact"
|
||||
id="request-jscontact"
|
||||
type="checkbox"
|
||||
/>
|
||||
Request JSContact
|
||||
</label>
|
||||
|
||||
<label htmlFor="follow-referral">
|
||||
<input
|
||||
name="follow-referral"
|
||||
id="follow-referral"
|
||||
type="checkbox"
|
||||
/>
|
||||
Follow referral to registrar's RDAP record
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="output-div">
|
||||
{response != null ? <Generic data={response} /> : null}
|
||||
</div>
|
||||
|
||||
<p>
|
||||
This page implements a <em>completely private lookup tool</em> for
|
||||
domain names, IP addresses and Autonymous System Numbers (ASNs).
|
||||
Only the relevant registry sees your query: your browser will
|
||||
directly connect to the registry's RDAP server using an
|
||||
encrypted HTTPS connection to protect the confidentiality of your
|
||||
query. If you click the "Follow referral to registrar's
|
||||
RDAP record" checkbox, then the sponsoring registrar may also
|
||||
see your query.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="https://about.rdap.org" target="_new">
|
||||
Click here
|
||||
</a>{" "}
|
||||
for more information about what RDAP is and how it differs from
|
||||
traditional Whois.
|
||||
</li>
|
||||
<li>
|
||||
Most generic TLDs now support RDAP, but only a few ccTLDs have
|
||||
deployed RDAP so far. To see which TLDs support RDAP,{" "}
|
||||
<a href="https://deployment.rdap.org" target="_new">
|
||||
click here
|
||||
</a>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
There is no bootstrap registry for top-level domains or
|
||||
ICANN-accredited registrars; instead these queries are sent to the
|
||||
<a href="https://about.rdap.org/#additional" target="_new">
|
||||
{"{"}root,registrars{"}"}.rdap.org servers
|
||||
</a>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
To submit feedback,{" "}
|
||||
<a href="mailto:feedback@rdap.org">click here</a>. Please contact
|
||||
the relevant registry or registrar if you have an issue with the
|
||||
content of an RDAP response.
|
||||
</li>
|
||||
<li>
|
||||
This tool is Free Software; for the license,{" "}
|
||||
<a href="LICENSE">click here</a>. To fork a copy of the git
|
||||
repository,{" "}
|
||||
<a
|
||||
rel="noopener"
|
||||
target="_new"
|
||||
href="https://gitlab.centralnic.com/centralnic/rdap-web-client"
|
||||
>
|
||||
click here
|
||||
</a>
|
||||
.
|
||||
</li>
|
||||
<li>
|
||||
This page uses{" "}
|
||||
<a
|
||||
rel="noopener"
|
||||
target="_new"
|
||||
href="https://github.com/whitequark/ipaddr.js/"
|
||||
>
|
||||
ipaddr.js
|
||||
</a>{" "}
|
||||
by{" "}
|
||||
<a rel="noopener" target="_new" href="https://whitequark.org/">
|
||||
whitequark
|
||||
</a>
|
||||
.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Old;
|
||||
36
src/styles/globals.css
Normal file
36
src/styles/globals.css
Normal file
@@ -0,0 +1,36 @@
|
||||
@import "tailwindcss";
|
||||
@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Inter:wght@100..900&display=swap");
|
||||
|
||||
@theme {
|
||||
--font-sans: "Inter var", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--color-zinc-850: #1D1D20;
|
||||
}
|
||||
|
||||
@source "../**/*.{js,ts,jsx,tsx}";
|
||||
|
||||
dd {
|
||||
margin: 0.5em 0 1em 2em;
|
||||
}
|
||||
|
||||
.dashed {
|
||||
border-bottom: 1px dashed silver;
|
||||
}
|
||||
|
||||
body {
|
||||
color-scheme: dark;
|
||||
@apply bg-zinc-900 font-sans text-white;
|
||||
}
|
||||
|
||||
dd,
|
||||
dl {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.scrollbar-thin {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700\&family=Inter:wght@100..900&display=swap");
|
||||
|
||||
dd {
|
||||
margin: 0.5em 0 1em 2em;
|
||||
}
|
||||
|
||||
.dashed {
|
||||
border-bottom: 1px dashed silver;
|
||||
}
|
||||
|
||||
body {
|
||||
color-scheme: dark;
|
||||
@apply bg-zinc-900 font-sans text-white;
|
||||
}
|
||||
|
||||
dd,
|
||||
dl {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.scrollbar-thin {
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
const defaultTheme = require("tailwindcss/defaultTheme");
|
||||
|
||||
module.exports = {
|
||||
content: ["./src/**/*.{js,ts,jsx,tsx}"],
|
||||
darkMode: "class",
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
mono: ["IBM Plex Mono", ...defaultTheme.fontFamily.mono],
|
||||
sans: ["Inter var", ...defaultTheme.fontFamily.sans],
|
||||
},
|
||||
colors: {
|
||||
zinc: {
|
||||
850: "#1D1D20",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
Reference in New Issue
Block a user