Establish NODE.DC design system baseline

This commit is contained in:
DCCONSTRUCTIONS
2026-07-10 02:34:36 +03:00
commit 73629d68c3
63 changed files with 6701 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
export type RgbTuple = readonly [number, number, number];
export interface AccentVariables {
"--nodedc-accent-rgb": string;
"--nodedc-on-accent-rgb": string;
}
function clampChannel(value: number): number {
return Math.max(0, Math.min(255, Math.round(value)));
}
function linearChannel(channel: number): number {
const normalized = clampChannel(channel) / 255;
return normalized <= 0.04045
? normalized / 12.92
: ((normalized + 0.055) / 1.055) ** 2.4;
}
export function relativeLuminance(rgb: RgbTuple): number {
const [red, green, blue] = rgb.map(linearChannel) as [number, number, number];
return red * 0.2126 + green * 0.7152 + blue * 0.0722;
}
export function readableTextRgb(rgb: RgbTuple): RgbTuple {
return relativeLuminance(rgb) > 0.46 ? [11, 17, 23] : [247, 248, 244];
}
export function rgbTuple(value: RgbTuple): string {
return value.map(clampChannel).join(" ");
}
export function createAccentVariables(accent: RgbTuple): AccentVariables {
return {
"--nodedc-accent-rgb": rgbTuple(accent),
"--nodedc-on-accent-rgb": rgbTuple(readableTextRgb(accent)),
};
}
+67
View File
@@ -0,0 +1,67 @@
export type FloatingPlacement = "bottom-start" | "bottom-end" | "top-start" | "top-end";
export interface RectLike {
top: number;
right: number;
bottom: number;
left: number;
width: number;
height: number;
}
export interface FloatingPositionOptions {
anchor: RectLike;
surfaceWidth: number;
surfaceHeight: number;
placement?: FloatingPlacement;
offset?: number;
viewportWidth?: number;
viewportHeight?: number;
viewportPadding?: number;
}
export interface FloatingPosition {
top: number;
left: number;
maxHeight: number;
resolvedPlacement: FloatingPlacement;
}
export function computeFloatingPosition({
anchor,
surfaceWidth,
surfaceHeight,
placement = "bottom-start",
offset = 8,
viewportWidth = typeof window === "undefined" ? 1920 : window.innerWidth,
viewportHeight = typeof window === "undefined" ? 1080 : window.innerHeight,
viewportPadding = 8,
}: FloatingPositionOptions): FloatingPosition {
const roomBelow = viewportHeight - anchor.bottom - offset - viewportPadding;
const roomAbove = anchor.top - offset - viewportPadding;
const requestedTop = placement.startsWith("top");
const shouldFlip = requestedTop
? surfaceHeight > roomAbove && roomBelow > roomAbove
: surfaceHeight > roomBelow && roomAbove > roomBelow;
const resolvedPlacement = shouldFlip
? (`${requestedTop ? "bottom" : "top"}-${placement.endsWith("end") ? "end" : "start"}` as FloatingPlacement)
: placement;
const alignedLeft = resolvedPlacement.endsWith("end")
? anchor.right - surfaceWidth
: anchor.left;
const left = Math.min(
Math.max(viewportPadding, alignedLeft),
Math.max(viewportPadding, viewportWidth - surfaceWidth - viewportPadding),
);
const availableHeight = resolvedPlacement.startsWith("top") ? roomAbove : roomBelow;
const maxHeight = Math.max(96, availableHeight);
const visibleHeight = Math.min(surfaceHeight, maxHeight);
const top = resolvedPlacement.startsWith("top")
? Math.max(viewportPadding, anchor.top - offset - visibleHeight)
: Math.min(anchor.bottom + offset, viewportHeight - visibleHeight - viewportPadding);
return { top, left, maxHeight, resolvedPlacement };
}
+4
View File
@@ -0,0 +1,4 @@
export * from "./accent";
export * from "./floating";
export * from "./theme";
+28
View File
@@ -0,0 +1,28 @@
import { createAccentVariables, type RgbTuple } from "./accent";
export type NodedcTheme = "dark" | "light";
export interface ApplyThemeOptions {
theme?: NodedcTheme;
accent?: RgbTuple;
}
export function applyNodedcTheme(
target: HTMLElement,
{ theme = "dark", accent }: ApplyThemeOptions = {},
): void {
target.dataset.nodedcTheme = theme;
target.dataset.nodedcUi = "true";
if (!accent) return;
const variables = createAccentVariables(accent);
Object.entries(variables).forEach(([property, value]) => {
target.style.setProperty(property, value);
});
}
export function clearNodedcAccent(target: HTMLElement): void {
target.style.removeProperty("--nodedc-accent-rgb");
target.style.removeProperty("--nodedc-on-accent-rgb");
}