add: some components, fonts

This commit is contained in:
alikia2x (寒寒) 2025-05-27 22:33:28 +08:00
parent 16cfae8bad
commit 557a013b42
Signed by: alikia2x
GPG Key ID: 56209E0CCD8420C6
61 changed files with 1079 additions and 139 deletions

BIN
packages/next/app/fonts/InterFont/Inter-Black.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-BlackItalic.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-Bold.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-BoldItalic.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-ExtraBold.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-ExtraLight.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-Italic.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-Light.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-LightItalic.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-Medium.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-MediumItalic.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-Regular.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-SemiBold.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-SemiBoldItalic.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-Thin.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/Inter-ThinItalic.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,450 @@
@font-face {
font-family: InterVariable;
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url("InterVariable.woff2") format("woff2");
}
@font-face {
font-family: InterVariable;
font-style: italic;
font-weight: 100 900;
font-display: swap;
src: url("InterVariable-Italic.woff2") format("woff2");
}
/* static fonts */
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 100;
font-display: swap;
src: url("Inter-Thin.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 100;
font-display: swap;
src: url("Inter-ThinItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 200;
font-display: swap;
src: url("Inter-ExtraLight.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 200;
font-display: swap;
src: url("Inter-ExtraLightItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("Inter-Light.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 300;
font-display: swap;
src: url("Inter-LightItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("Inter-Regular.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 400;
font-display: swap;
src: url("Inter-Italic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("Inter-Medium.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 500;
font-display: swap;
src: url("Inter-MediumItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("Inter-SemiBold.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 600;
font-display: swap;
src: url("Inter-SemiBoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("Inter-Bold.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 700;
font-display: swap;
src: url("Inter-BoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("Inter-ExtraBold.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 800;
font-display: swap;
src: url("Inter-ExtraBoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: normal;
font-weight: 900;
font-display: swap;
src: url("Inter-Black.woff2") format("woff2");
}
@font-face {
font-family: "Inter";
font-style: italic;
font-weight: 900;
font-display: swap;
src: url("Inter-BlackItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 100;
font-display: swap;
src: url("InterDisplay-Thin.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 100;
font-display: swap;
src: url("InterDisplay-ThinItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 200;
font-display: swap;
src: url("InterDisplay-ExtraLight.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 200;
font-display: swap;
src: url("InterDisplay-ExtraLightItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("InterDisplay-Light.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 300;
font-display: swap;
src: url("InterDisplay-LightItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("InterDisplay-Regular.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 400;
font-display: swap;
src: url("InterDisplay-Italic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("InterDisplay-Medium.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 500;
font-display: swap;
src: url("InterDisplay-MediumItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("InterDisplay-SemiBold.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 600;
font-display: swap;
src: url("InterDisplay-SemiBoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("InterDisplay-Bold.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 700;
font-display: swap;
src: url("InterDisplay-BoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("InterDisplay-ExtraBold.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 800;
font-display: swap;
src: url("InterDisplay-ExtraBoldItalic.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: normal;
font-weight: 900;
font-display: swap;
src: url("InterDisplay-Black.woff2") format("woff2");
}
@font-face {
font-family: "InterDisplay";
font-style: italic;
font-weight: 900;
font-display: swap;
src: url("InterDisplay-BlackItalic.woff2") format("woff2");
}
@font-feature-values InterVariable {
@character-variant {
cv01: 1;
cv02: 2;
cv03: 3;
cv04: 4;
cv05: 5;
cv06: 6;
cv07: 7;
cv08: 8;
cv09: 9;
cv10: 10;
cv11: 11;
cv12: 12;
cv13: 13;
alt-1: 1; /* Alternate one */
alt-3: 9; /* Flat-top three */
open-4: 2; /* Open four */
open-6: 3; /* Open six */
open-9: 4; /* Open nine */
lc-l-with-tail: 5; /* Lower-case L with tail */
simplified-u: 6; /* Simplified u */
alt-double-s: 7; /* Alternate German double s */
uc-i-with-serif: 8; /* Upper-case i with serif */
uc-g-with-spur: 10; /* Capital G with spur */
single-story-a: 11; /* Single-story a */
compact-lc-f: 12; /* Compact f */
compact-lc-t: 13; /* Compact t */
}
@styleset {
ss01: 1;
ss02: 2;
ss03: 3;
ss04: 4;
ss05: 5;
ss06: 6;
ss07: 7;
ss08: 8;
open-digits: 1; /* Open digits */
disambiguation: 2; /* Disambiguation (with zero) */
disambiguation-except-zero: 4; /* Disambiguation (no zero) */
round-quotes-and-commas: 3; /* Round quotes & commas */
square-punctuation: 7; /* Square punctuation */
square-quotes: 8; /* Square quotes */
circled-characters: 5; /* Circled characters */
squared-characters: 6; /* Squared characters */
}
}
@font-feature-values Inter {
@character-variant {
cv01: 1;
cv02: 2;
cv03: 3;
cv04: 4;
cv05: 5;
cv06: 6;
cv07: 7;
cv08: 8;
cv09: 9;
cv10: 10;
cv11: 11;
cv12: 12;
cv13: 13;
alt-1: 1; /* Alternate one */
alt-3: 9; /* Flat-top three */
open-4: 2; /* Open four */
open-6: 3; /* Open six */
open-9: 4; /* Open nine */
lc-l-with-tail: 5; /* Lower-case L with tail */
simplified-u: 6; /* Simplified u */
alt-double-s: 7; /* Alternate German double s */
uc-i-with-serif: 8; /* Upper-case i with serif */
uc-g-with-spur: 10; /* Capital G with spur */
single-story-a: 11; /* Single-story a */
compact-lc-f: 12; /* Compact f */
compact-lc-t: 13; /* Compact t */
}
@styleset {
ss01: 1;
ss02: 2;
ss03: 3;
ss04: 4;
ss05: 5;
ss06: 6;
ss07: 7;
ss08: 8;
open-digits: 1; /* Open digits */
disambiguation: 2; /* Disambiguation (with zero) */
disambiguation-except-zero: 4; /* Disambiguation (no zero) */
round-quotes-and-commas: 3; /* Round quotes & commas */
square-punctuation: 7; /* Square punctuation */
square-quotes: 8; /* Square quotes */
circled-characters: 5; /* Circled characters */
squared-characters: 6; /* Squared characters */
}
}
@font-feature-values InterDisplay {
@character-variant {
cv01: 1;
cv02: 2;
cv03: 3;
cv04: 4;
cv05: 5;
cv06: 6;
cv07: 7;
cv08: 8;
cv09: 9;
cv10: 10;
cv11: 11;
cv12: 12;
cv13: 13;
alt-1: 1; /* Alternate one */
alt-3: 9; /* Flat-top three */
open-4: 2; /* Open four */
open-6: 3; /* Open six */
open-9: 4; /* Open nine */
lc-l-with-tail: 5; /* Lower-case L with tail */
simplified-u: 6; /* Simplified u */
alt-double-s: 7; /* Alternate German double s */
uc-i-with-serif: 8; /* Upper-case i with serif */
uc-g-with-spur: 10; /* Capital G with spur */
single-story-a: 11; /* Single-story a */
compact-lc-f: 12; /* Compact f */
compact-lc-t: 13; /* Compact t */
}
@styleset {
ss01: 1;
ss02: 2;
ss03: 3;
ss04: 4;
ss05: 5;
ss06: 6;
ss07: 7;
ss08: 8;
open-digits: 1; /* Open digits */
disambiguation: 2; /* Disambiguation (with zero) */
disambiguation-except-zero: 4; /* Disambiguation (no zero) */
round-quotes-and-commas: 3; /* Round quotes & commas */
square-punctuation: 7; /* Square punctuation */
square-quotes: 8; /* Square quotes */
circled-characters: 5; /* Circled characters */
squared-characters: 6; /* Squared characters */
}
}

BIN
packages/next/app/fonts/InterFont/InterDisplay-Black.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/InterDisplay-Bold.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/InterDisplay-Italic.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/InterDisplay-Light.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/InterDisplay-Medium.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/InterDisplay-Regular.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/InterDisplay-Thin.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/InterVariable-Italic.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/InterFont/InterVariable.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/MiSans/MiSans VF.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/MiSans/MiSans-Bold.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/MiSans/MiSans-Demibold.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/MiSans/MiSans-ExtraLight.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/MiSans/MiSans-Heavy.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/MiSans/MiSans-Light.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/MiSans/MiSans-Medium.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/MiSans/MiSans-Normal.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/MiSans/MiSans-Regular.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/MiSans/MiSans-Semibold.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

BIN
packages/next/app/fonts/MiSans/MiSans-Thin.woff2 (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,88 @@
@font-face {
font-family: "MiSans VF";
font-style: normal;
font-weight: 150 700;
font-display: swap;
src: url("MiSans VF.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 100;
font-display: swap;
src: url("MiSans-Thin.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 200;
font-display: swap;
src: url("MiSans-ExtraLight.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("MiSans-Light.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 360;
font-display: swap;
src: url("MiSans-Normal.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("MiSans-Regular.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("MiSans-Medium.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("MiSans-Demibold.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("MiSans-Semibold.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("MiSans-Bold.woff2") format("woff2");
}
@font-face {
font-family: "MiSans";
font-style: normal;
font-weight: 900;
font-display: swap;
src: url("MiSans-Heavy.woff2") format("woff2");
}

View File

@ -1,105 +1,84 @@
@import url("./fonts/InterFont/Inter.css");
@import url("./fonts/MiSans/MiSans.css");
@import "tailwindcss";
@theme {
--color-surface-container-high: #f7e4e1;
--color-on-surface-variant: #534341;
--color-dark-on-surface-variant: #d8c2be;
--color-dark-surface-container-high: #322826;
--color-dark-surface-container: #271d1c;
--color-surface-container: #fceae7;
--color-on-surface: #231918;
--color-dark-on-surface: #f1dfdc;
--color-background: #fff8f6;
--color-on-background: #2a1613;
--color-surface: #fff8f6;
--color-dark-surface: #1a1110;
--color-primary: #904b40;
--color-dark-primary: #ffb2b7;
--color-primary-container: #ffdad4;
--color-dark-primary-container: #73342a;
--color-surface-dim: #f7d2cc;
--color-surface-bright: #fff8f6;
--color-surface-container-lowest: #ffffff;
--color-surface-container-low: #fff0ee;
--color-surface-container: #ffe9e6;
--color-surface-container-high: #ffe2dd;
--color-surface-container-highest: #ffdad4;
--color-on-surface: #2a1613;
--color-surface-variant: #ffdad4;
--color-on-surface-variant: #5f3e39;
--color-inverse-surface: #422b27;
--color-inverse-on-surface: #ffedea;
--color-outline: #946e68;
--color-outline-variant: #eabcb4;
--color-shadow: #000000;
--color-scrim: #000000;
--color-surface-tint: #c00100;
--color-primary: #a50100;
--color-on-primary: #ffffff;
--color-dark-on-primary: #561e16;
--color-dark-primary-fixed-dim: #ffb4a8;
--color-secondary-container: #ffdad4;
--color-dark-secondary-container: #5d3f3b;
--color-primary-container: #eb0000;
--color-on-primary-container: #ffffff;
--color-inverse-primary: #ffb4a8;
--color-secondary: #b4271a;
--color-on-secondary: #ffffff;
--color-secondary-container: #ff7460;
--color-on-secondary-container: #2f0000;
--color-tertiary: #6f4800;
--color-on-tertiary: #ffffff;
--color-tertiary-container: #9f6900;
--color-on-tertiary-container: #ffffff;
--color-error: #ba1a1a;
--color-on-error: #ffffff;
--color-error-container: #ffdad6;
--color-on-error-container: #410002;
--color-surface-tint: rgb(144 75 64);
--color-dark-surface-tint: rgb(255 180 168);
--color-on-primary-container: rgb(115 52 42);
--color-dark-on-primary-container: rgb(255 218 212);
--color-secondary: rgb(119 86 81);
--color-dark-secondary: rgb(231 189 182);
--color-on-secondary: rgb(255 255 255);
--color-dark-on-secondary: rgb(68 41 37);
--color-on-secondary-container: rgb(93 63 59);
--color-dark-on-secondary-container: rgb(255 218 212);
--color-tertiary: rgb(112 92 46);
--color-dark-tertiary: rgb(222 196 140);
--color-on-tertiary: rgb(255 255 255);
--color-dark-on-tertiary: rgb(62 46 4);
--color-tertiary-container: rgb(251 223 166);
--color-dark-tertiary-container: rgb(86 68 25);
--color-on-tertiary-container: rgb(86 68 25);
--color-dark-on-tertiary-container: rgb(251 223 166);
--color-error: rgb(186 26 26);
--color-dark-error: rgb(255 180 171);
--color-on-error: rgb(255 255 255);
--color-dark-on-error: rgb(105 0 5);
--color-error-container: rgb(255 218 214);
--color-dark-error-container: rgb(147 0 10);
--color-on-error-container: rgb(147 0 10);
--color-dark-on-error-container: rgb(255 218 214);
--color-background: rgb(255 248 246);
--color-dark-background: rgb(26 17 16);
--color-on-background: rgb(35 25 24);
--color-dark-on-background: rgb(241 223 220);
--color-surface-variant: rgb(245 221 218);
--color-dark-surface-variant: rgb(83 67 65);
--color-outline: rgb(133 115 112);
--color-dark-outline: rgb(160 140 137);
--color-outline-variant: rgb(216 194 190);
--color-dark-outline-variant: rgb(83 67 65);
--color-shadow: rgb(0 0 0);
--color-dark-shadow: rgb(0 0 0);
--color-scrim: rgb(0 0 0);
--color-dark-scrim: rgb(0 0 0);
--color-inverse-surface: rgb(57 46 44);
--color-dark-inverse-surface: rgb(241 223 220);
--color-inverse-on-surface: rgb(255 237 234);
--color-dark-inverse-on-surface: rgb(57 46 44);
--color-inverse-primary: rgb(255 180 168);
--color-dark-inverse-primary: rgb(144 75 64);
--color-primary-fixed: rgb(255 218 212);
--color-dark-primary-fixed: rgb(255 218 212);
--color-on-primary-fixed: rgb(58 9 5);
--color-dark-on-primary-fixed: rgb(58 9 5);
--color-primary-fixed-dim: rgb(255 180 168);
--color-on-primary-fixed-variant: rgb(115 52 42);
--color-dark-on-primary-fixed-variant: rgb(115 52 42);
--color-secondary-fixed: rgb(255 218 212);
--color-dark-secondary-fixed: rgb(255 218 212);
--color-on-secondary-fixed: rgb(44 21 18);
--color-dark-on-secondary-fixed: rgb(44 21 18);
--color-secondary-fixed-dim: rgb(231 189 182);
--color-dark-secondary-fixed-dim: rgb(231 189 182);
--color-on-secondary-fixed-variant: rgb(93 63 59);
--color-dark-on-secondary-fixed-variant: rgb(93 63 59);
--color-tertiary-fixed: rgb(251 223 166);
--color-dark-tertiary-fixed: rgb(251 223 166);
--color-on-tertiary-fixed: rgb(37 26 0);
--color-dark-on-tertiary-fixed: rgb(37 26 0);
--color-tertiary-fixed-dim: rgb(222 196 140);
--color-dark-tertiary-fixed-dim: rgb(222 196 140);
--color-on-tertiary-fixed-variant: rgb(86 68 25);
--color-dark-on-tertiary-fixed-variant: rgb(86 68 25);
--color-surface-dim: rgb(232 214 211);
--color-dark-surface-dim: rgb(26 17 16);
--color-surface-bright: rgb(255 248 246);
--color-dark-surface-bright: rgb(66 55 53);
--color-surface-container-lowest: rgb(255 255 255);
--color-dark-surface-container-lowest: rgb(20 12 11);
--color-surface-container-low: rgb(255 240 238);
--color-dark-surface-container-low: rgb(35 25 24);
--color-surface-container-highest: rgb(241 223 220);
--color-dark-surface-container-highest: rgb(61 50 48);
--color-dark-background: #210e0b;
--color-dark-on-background: #ffdad4;
--color-dark-surface: #210e0b;
--color-dark-surface-dim: #210e0b;
--color-dark-surface-bright: #4b332f;
--color-dark-surface-container-lowest: #1b0907;
--color-dark-surface-container-low: #2a1613;
--color-dark-surface-container: #2f1a17;
--color-dark-surface-container-high: #3a2421;
--color-dark-surface-container-highest: #462f2b;
--color-dark-on-surface: #ffdad4;
--color-dark-surface-variant: #5f3e39;
--color-dark-on-surface-variant: #eabcb4;
--color-dark-inverse-surface: #ffdad4;
--color-dark-inverse-on-surface: #422b27;
--color-dark-outline: #b08780;
--color-dark-outline-variant: #5f3e39;
--color-dark-shadow: #000000;
--color-dark-scrim: #000000;
--color-dark-surface-tint: #ffb4a8;
--color-dark-primary: #ffb4a8;
--color-dark-on-primary: #690000;
--color-dark-primary-container: #de0000;
--color-dark-on-primary-container: #ffffff;
--color-dark-inverse-primary: #c00100;
--color-dark-secondary: #ffb4a8;
--color-dark-on-secondary: #690000;
--color-dark-secondary-container: #870100;
--color-dark-on-secondary-container: #ffc9c0;
--color-dark-tertiary: #feba54;
--color-dark-on-tertiary: #452b00;
--color-dark-tertiary-container: #966300;
--color-dark-on-tertiary-container: #ffffff;
--color-dark-error: #ffb4ab;
--color-dark-on-error: #690005;
--color-dark-error-container: #93000a;
--color-dark-on-error-container: #ffdad6;
--font-zh: "InterVariable", "MiSans VF", sans-serif;
}

View File

@ -6,6 +6,9 @@ import LoadingSpinner from "@/components/icons/LoadingSpinner";
import { computeVdfInWorker } from "@/lib/vdf";
import useSWR from "swr";
import { ApiRequestError } from "@/lib/net";
import { Portal } from "@/components/utils/Portal";
import { Dialog, DialogHeadline, DialogSupportingText } from "@/components/ui/Dialog";
import { FilledButton } from "@/components/ui/Buttons/FilledButton";
interface CaptchaSessionResponse {
g: string;
@ -38,6 +41,8 @@ const SignUpForm: React.FC<RegistrationFormProps> = ({ backendURL }) => {
const [password, setPassword] = useState("");
const [nickname, setNickname] = useState("");
const [loading, setLoading] = useState(false);
const [showDialog, setShowDialog] = useState(false);
const [dialogContent, setDialogContent] = useState(<></>);
const {
data: captchaSession,
@ -83,15 +88,13 @@ const SignUpForm: React.FC<RegistrationFormProps> = ({ backendURL }) => {
const registrationResponse = await fetch(registrationUrl.toString(), {
method: "POST",
headers: {
"Content-Type": "application/json"
"Content-Type": "application/json",
Authorization: `Bearer ${captchaResult.token}`
},
body: JSON.stringify({
username,
password,
nickname,
// Include the captcha result if needed by the backend
captchaId: captchaSession.id,
captchaAnswer: ans.result.toString()
nickname
})
});
@ -141,15 +144,32 @@ const SignUpForm: React.FC<RegistrationFormProps> = ({ backendURL }) => {
supportingText="昵称可以重复。"
maxChar={30}
/>
<button
className="bg-primary dark:bg-dark-primary text-on-primary dark:text-dark-on-primary duration-150
rounded-full hover:bg-on-primary-container hover:dark:bg-dark-on-primary-container mt-2
flex items-center text-sm leading-5 justify-center h-10 w-full"
type="submit"
disabled={loading}
<FilledButton
type="button"
onClick={() => {
setShowDialog(true);
setDialogContent(
<>
<DialogHeadline>Error</DialogHeadline>
<DialogSupportingText>
<p>Your operation frequency is too high. Please try again later. (RATE_LIMIT_EXCEED)</p>
</DialogSupportingText>
</>
);
}}
size="m"
shape="square"
>
Show Dialog
</FilledButton>
<FilledButton type="submit" disabled={loading}>
{!loading ? <span></span> : <LoadingSpinner />}
</button>
</FilledButton>
<Portal>
<Dialog show={showDialog} onClose={() => setShowDialog(false)}>
{dialogContent}
</Dialog>
</Portal>
</form>
);
};

View File

@ -6,14 +6,17 @@ import LogoMobileLight from "@/public/icons/TitleBar Mobile Light.svg";
import LogoMobileDark from "@/public/icons/TitleBar Mobile Dark.svg";
import DarkModeImage from "@/components/utils/DarkModeImage";
import React, { useState } from "react";
import { NavigationDrawer } from "@/components/shell/NavigatinDrawer";
import { NavigationDrawer } from "@/components/ui/NavigatinDrawer";
import { Portal } from "@/components/utils/Portal";
import { RegisterIcon } from "@/components/icons/RegisterIcon";
import { SearchBox } from "@/components/ui/SearchBox";
import { MenuIcon } from "@/components/icons/MenuIcon";
import { SearchIcon } from "@/components/icons/SearchIcon";
import { InfoIcon } from "../icons/InfoIcon";
import { HomeIcon } from "../icons/HomeIcon";
import { InfoIcon } from "@/components/icons/InfoIcon";
import { HomeIcon } from "@/components/icons/HomeIcon";
import { TextButton } from "@/components/ui/Buttons/TextButton";
import { useRouter } from "next/navigation";
import Link from "next/link";
export const HeaderDestop = () => {
return (
@ -45,37 +48,46 @@ export const HeaderDestop = () => {
export const HeaderMobile = () => {
const [showDrawer, setShowDrawer] = useState(false);
const [showsearchBox, setShowsearchBox] = useState(false);
return (
<>
<Portal>
<NavigationDrawer show={showDrawer} onClose={() => setShowDrawer(false)}>
<div className="flex flex-col w-full">
<div className="w-full h-14 flex items-center px-4 mt-1 pl-5">
<div className="flex flex-col w-full gap-2">
<div className="w-full h-14 flex items-center px-4 mt-3 pl-6">
<DarkModeImage
lightSrc={LogoMobileLight}
darkSrc={LogoMobileDark}
alt="Logo"
className="w-24 h-8"
className="w-30 h-10"
/>
</div>
<div className="w-full h-14 flex items-center px-4">
<a href="/" className="flex">
<HomeIcon className="text-2xl pr-4" />
<span></span>
</a>
</div>
<div className="w-full h-14 flex items-center px-4">
<a href="/about" className="flex">
<InfoIcon className="text-2xl pr-4" />
<span></span>
</a>
</div>
<div className="w-full h-14 flex items-center px-4">
<a href="/signup" className="flex">
<RegisterIcon className="text-2xl pr-4" />
<span></span>
</a>
</div>
<Link href="/">
<TextButton className="w-full h-14 flex px-4 justify-start" size="m">
<div className="flex items-center">
<HomeIcon className="text-2xl pr-4" />
<span></span>
</div>
</TextButton>
</Link>
<Link href="/about">
<TextButton className="w-full h-14 flex px-4 justify-start" size="m">
<div className="flex items-center">
<InfoIcon className="text-2xl pr-4" />
<span></span>
</div>
</TextButton>
</Link>
<Link href="/signup">
<TextButton className="w-full h-14 flex px-4 justify-start" size="m">
<div className="flex items-center">
<RegisterIcon className="text-2xl pr-4" />
<span></span>
</div>
</TextButton>
</Link>
</div>
</NavigationDrawer>
</Portal>

View File

@ -0,0 +1,41 @@
import useRipple from "@/components/utils/useRipple";
interface FilledButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
size?: "xs" | "s" | "m" | "l" | "xl";
shape?: "round" | "square";
children?: React.ReactNode;
ripple?: boolean;
}
export const FilledButton = ({
children,
size = "s",
shape = "round",
className,
ripple = true,
...rest
}: FilledButtonProps) => {
let sizeClasses = "text-sm leading-5 h-10 px-4";
let shapeClasses = "rounded-full";
if (size === "m") {
sizeClasses = "text-base leading-6 h-14 px-6";
shapeClasses = shape === "round" ? "rounded-full" : "rounded-2xl";
}
const { onMouseDown, onTouchStart } = useRipple({ ripple });
return (
<button
className={`bg-primary dark:bg-dark-primary text-on-primary dark:text-dark-on-primary duration-150 select-none
flex items-center justify-center relative overflow-hidden
${sizeClasses} ${shapeClasses} ${className}`}
{...rest}
onMouseDown={onMouseDown}
onTouchStart={onTouchStart}
>
<div className="absolute w-full h-full hover:bg-on-surface-variant/10"></div>
{children}
</button>
);
};

View File

@ -0,0 +1,41 @@
import useRipple from "@/components/utils/useRipple";
interface TextButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
size?: "xs" | "s" | "m" | "l" | "xl";
shape?: "round" | "square";
children?: React.ReactNode;
ripple?: boolean;
}
export const TextButton = ({
children,
size = "s",
shape = "round",
className,
ripple = true,
...rest
}: TextButtonProps) => {
let sizeClasses = "text-sm leading-5 h-10 px-4";
let shapeClasses = "rounded-full";
if (size === "m") {
sizeClasses = "text-base leading-6 h-14 px-6";
shapeClasses = shape === "round" ? "rounded-full" : "rounded-2xl";
}
const { onMouseDown, onTouchStart } = useRipple({ ripple });
return (
<button
className={`text-primary dark:text-dark-primary duration-150 select-none
flex items-center justify-center relative overflow-hidden
${sizeClasses} ${shapeClasses} ${className}`}
{...rest}
onMouseDown={onMouseDown}
onTouchStart={onTouchStart}
>
<div className="absolute w-full h-full hover:bg-primary/10"></div>
{children}
</button>
);
};

View File

@ -0,0 +1,57 @@
import { motion, AnimatePresence } from "framer-motion";
import React, { useEffect, useRef } from "react";
import { TextButton } from "./Buttons/TextButton";
interface DialogProps {
show: boolean;
onClose: () => void;
children?: React.ReactNode;
}
interface DialogHeadlineProps {
children?: React.ReactNode;
}
interface DialogSupportingTextProps {
children?: React.ReactNode;
}
export const DialogHeadline: React.FC<DialogHeadlineProps> = ({ children }: DialogHeadlineProps) => {
return <h2 className="text-2xl leading-8 text-on-surface dark:text-dark-on-surface">{children}</h2>;
};
export const DialogSupportingText: React.FC<DialogSupportingTextProps> = ({ children }: DialogHeadlineProps) => {
return <div className="mt-4 text-sm leading-5 mb-6">{children}</div>;
};
export const Dialog: React.FC<DialogProps> = ({ show, onClose, children }: DialogProps) => {
return (
<AnimatePresence>
{show && (
<div className="w-full h-full top-0 left-0 absolute flex items-center justify-center">
<motion.div
className="fixed top-0 left-0 w-full h-full z-40 bg-black/20 pointer-none"
aria-hidden="true"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.3 }}
/>
<motion.div
className="fixed min-w-[17.5rem] sm:max-w-[35rem] h-auto z-50 bg-surface-container-high
shadow-xl shadow-shadow/5 rounded-[1.75rem] p-6 dark:bg-dark-surface-container-high mx-2"
initial={{ opacity: 0.5, transform: "scale(0.9)" }}
animate={{ opacity: 1, transform: "scale(1)" }}
exit={{ opacity: 0 }}
transition={{ ease: [0.31, 0.69, 0.3, 1.02], duration: 0.3 }}
>
{children}
<div className="flex justify-end gap-2">
<TextButton onClick={onClose}>Action 1</TextButton>
<TextButton onClick={onClose}>Action 2</TextButton>
</div>
</motion.div>
</div>
)}
</AnimatePresence>
);
};

View File

@ -8,11 +8,11 @@ interface DrawerProps {
}
export const NavigationDrawer = ({ show = false, onClose, children }: DrawerProps) => {
const coverRef = useRef<HTMLDivElement>(null);
const scrimRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleOutsideClick = (event: MouseEvent) => {
if (show && coverRef.current && event.target === coverRef.current) {
if (show && scrimRef.current && event.target === scrimRef.current) {
onClose();
}
};
@ -27,9 +27,9 @@ export const NavigationDrawer = ({ show = false, onClose, children }: DrawerProp
<AnimatePresence>
{show && (
<>
{/* Backdrop - Fade in/out */}
{/* Scrim - Fade in/out */}
<motion.div
ref={coverRef}
ref={scrimRef}
className="fixed top-0 left-0 w-full h-full z-40 bg-black/10"
aria-hidden="true"
initial={{ opacity: 0 }}
@ -41,12 +41,12 @@ export const NavigationDrawer = ({ show = false, onClose, children }: DrawerProp
{/* Drawer - Slide from left */}
<motion.div
className="fixed top-0 left-0 h-full bg-[#fff0ee] dark:bg-[#231918] z-50"
className="fixed top-0 left-0 h-full bg-surface-container-low dark:bg-dark-surface-container-low z-50"
style={{ width: "min(22.5rem, 70vw)" }}
initial={{ x: -500, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
exit={{ x: -500, opacity: 0 }}
transition={{ type: "spring", stiffness: 438, damping: 46 }}
transition={{ duration: 0.25, ease: ["easeOut", "easeOut"] }}
role="dialog"
aria-modal="true"
>

View File

@ -42,7 +42,7 @@ export const SearchBox: React.FC<SearchBoxProps> = ({ close = () => {} }) => {
>
<div
className="w-full h-10 lg:h-12 px-4 rounded-full bg-surface-container-high
dark:bg-zinc-800/70 backdrop-blur-lg flex justify-between md:px-5"
dark:bg-dark-surface-container-high backdrop-blur-lg flex justify-between md:px-5"
>
<button className="w-6" onClick={() => search(inputValue)}>
<SearchIcon

View File

@ -0,0 +1,15 @@
.ripple {
position: absolute;
border-radius: 50%;
transform: scale(0);
opacity: 0.2;
animation: ripple-effect 0.8s linear;
background-color: currentColor;
}
@keyframes ripple-effect {
to {
transform: scale(4);
opacity: 0;
}
}

View File

@ -0,0 +1,90 @@
import "./ripple.css";
import { useCallback, useRef } from "react";
interface UseRippleOptions {
ripple?: boolean;
}
const useRipple = ({ ripple = true }: UseRippleOptions = {}) => {
const isTouchEventRef = useRef(false);
const resetTouchFlag = useCallback(() => {
isTouchEventRef.current = false;
}, []);
const handleMouseDown = useCallback(
(event: React.MouseEvent<HTMLButtonElement>) => {
if (!ripple) return;
if (isTouchEventRef.current) {
// Skip mouse event if this was a touch interaction
return;
}
// Proceed with mouse ripple
createRippleEffect(event);
},
[ripple]
);
const handleTouchStart = useCallback(
(event: React.TouchEvent<HTMLButtonElement>) => {
if (!ripple) return;
isTouchEventRef.current = true;
// Proceed with touch ripple
createRippleEffect(event);
},
[ripple, resetTouchFlag]
);
const handleTouchEnd = useCallback(
(_event: React.TouchEvent<HTMLButtonElement>) => {
if (!ripple) return;
isTouchEventRef.current = false;
},
[ripple, resetTouchFlag]
);
const createRippleEffect = (event: React.MouseEvent<HTMLButtonElement> | React.TouchEvent<HTMLButtonElement>) => {
const target = event.currentTarget;
const rect = target.getBoundingClientRect();
let x = 0;
let y = 0;
if ("touches" in event) {
x = event.touches[0].clientX - rect.left;
y = event.touches[0].clientY - rect.top;
} else {
x = event.clientX - rect.left;
y = event.clientY - rect.top;
}
const diameter = Math.max(rect.width, rect.height);
const rippleElement = document.createElement("span");
rippleElement.style.width = `${diameter}px`;
rippleElement.style.height = `${diameter}px`;
rippleElement.style.left = `${x - diameter / 2}px`;
rippleElement.style.top = `${y - diameter / 2}px`;
rippleElement.classList.add("ripple");
target.appendChild(rippleElement);
rippleElement.addEventListener("animationend", () => {
rippleElement.remove();
});
};
return {
onMouseDown: ripple ? handleMouseDown : undefined,
onTouchStart: ripple ? handleTouchStart : undefined,
ontouchend: ripple ? handleTouchEnd : undefined
};
};
export default useRipple;