diff --git a/eslint.config.ts b/eslint.config.ts
index edb1bbb..e73d45e 100644
--- a/eslint.config.ts
+++ b/eslint.config.ts
@@ -17,4 +17,12 @@ export default defineConfigWithVueTs(
pluginVue.configs['flat/essential'],
vueTsConfigs.recommended,
+
+ {
+ name: 'app/vue-rules',
+ files: ['**/*.vue'],
+ rules: {
+ 'vue/multi-word-component-names': 'off',
+ },
+ },
)
diff --git a/index.html b/index.html
index 09287f9..73181a1 100644
--- a/index.html
+++ b/index.html
@@ -2,6 +2,7 @@
+
Vue Bits
diff --git a/package-lock.json b/package-lock.json
index 2e970af..f6a56bb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,9 @@
"name": "vue-bits",
"version": "0.0.0",
"dependencies": {
+ "gsap": "^3.13.0",
+ "ogl": "^1.0.11",
+ "primeicons": "^7.0.0",
"vue": "^3.5.17",
"vue-router": "^4.5.1"
},
@@ -3652,6 +3655,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/gsap": {
+ "version": "3.13.0",
+ "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.13.0.tgz",
+ "integrity": "sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==",
+ "license": "Standard 'no charge' license: https://gsap.com/standard-license."
+ },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -4574,6 +4583,12 @@
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
+ "node_modules/ogl": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ogl/-/ogl-1.0.11.tgz",
+ "integrity": "sha512-kUpC154AFfxi16pmZUK4jk3J+8zxwTWGPo03EoYA8QPbzikHoaC82n6pNTbd+oEaJonaE8aPWBlX7ad9zrqLsA==",
+ "license": "Unlicense"
+ },
"node_modules/open": {
"version": "10.1.2",
"resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz",
@@ -4817,6 +4832,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/primeicons": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz",
+ "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==",
+ "license": "MIT"
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
diff --git a/package.json b/package.json
index 256b9ad..e6a12cf 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,9 @@
"lint": "eslint . --fix"
},
"dependencies": {
+ "gsap": "^3.13.0",
+ "ogl": "^1.0.11",
+ "primeicons": "^7.0.0",
"vue": "^3.5.17",
"vue-router": "^4.5.1"
},
diff --git a/public/assets/components.gif b/public/assets/components.gif
new file mode 100644
index 0000000..ba99128
Binary files /dev/null and b/public/assets/components.gif differ
diff --git a/public/assets/grain.webp b/public/assets/grain.webp
new file mode 100644
index 0000000..adbae00
Binary files /dev/null and b/public/assets/grain.webp differ
diff --git a/public/assets/messages.gif b/public/assets/messages.gif
new file mode 100644
index 0000000..da8d6d5
Binary files /dev/null and b/public/assets/messages.gif differ
diff --git a/src/App.vue b/src/App.vue
index e237e1a..4733645 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,3 +1,27 @@
- Vue Bits
+
+
+
+
+
+
+
diff --git a/src/assets/common/star.svg b/src/assets/common/star.svg
new file mode 100644
index 0000000..7b2f874
--- /dev/null
+++ b/src/assets/common/star.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/logos/vue-bits-logo-small-dark.svg b/src/assets/logos/vue-bits-logo-small-dark.svg
new file mode 100644
index 0000000..2f9ca97
--- /dev/null
+++ b/src/assets/logos/vue-bits-logo-small-dark.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/logos/vue-bits-logo-small.svg b/src/assets/logos/vue-bits-logo-small.svg
new file mode 100644
index 0000000..44571a6
--- /dev/null
+++ b/src/assets/logos/vue-bits-logo-small.svg
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/src/assets/logos/vue-bits-logo.svg b/src/assets/logos/vue-bits-logo.svg
new file mode 100644
index 0000000..048c6fb
--- /dev/null
+++ b/src/assets/logos/vue-bits-logo.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/logos/vuebits-gh-black.svg b/src/assets/logos/vuebits-gh-black.svg
new file mode 100644
index 0000000..7306df5
--- /dev/null
+++ b/src/assets/logos/vuebits-gh-black.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/logos/vuebits-gh-white.svg b/src/assets/logos/vuebits-gh-white.svg
new file mode 100644
index 0000000..8e329aa
--- /dev/null
+++ b/src/assets/logos/vuebits-gh-white.svg
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/common/Logo.vue b/src/components/common/Logo.vue
new file mode 100644
index 0000000..d121da7
--- /dev/null
+++ b/src/components/common/Logo.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/landing/DisplayHeader/DisplayHeader.css b/src/components/landing/DisplayHeader/DisplayHeader.css
new file mode 100644
index 0000000..648c46b
--- /dev/null
+++ b/src/components/landing/DisplayHeader/DisplayHeader.css
@@ -0,0 +1,310 @@
+.header {
+ display: flex;
+ align-items: center;
+ z-index: 100;
+ position: fixed;
+ top: 0;
+ left: 50%;
+ transform: translateX(-50%);
+ justify-content: space-between;
+ width: 100vw;
+ padding: 0 4em;
+ height: 160px;
+ margin: 0 auto;
+ background: linear-gradient(to bottom, #0e0e0e, transparent);
+}
+
+.header-container {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.nav-cta-group {
+ display: flex;
+ align-items: center;
+ gap: 1.5rem;
+}
+
+.logo {
+ position: relative;
+ z-index: 2;
+}
+
+.logo::before {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 200px;
+ height: 140px;
+ background: transparent;
+ backdrop-filter: blur(8px);
+ -webkit-backdrop-filter: blur(8px);
+ mask: radial-gradient(ellipse at center, black 0%, black 20%, transparent 80%);
+ -webkit-mask: radial-gradient(ellipse at center, black 0%, black 20%, transparent 80%);
+ z-index: -1;
+ pointer-events: none;
+}
+
+.logo svg {
+ height: 26px;
+ width: auto;
+ position: relative;
+ z-index: 2;
+}
+
+.landing-nav-items {
+ display: none;
+ color: #fff;
+ height: 60px;
+ padding: 0 2.4rem 0 calc(2.4rem + 6px);
+ border-radius: 50px;
+ border: 1px solid rgba(255, 255, 255, 0.07);
+ background: rgba(255, 255, 255, 0.01);
+ box-shadow: 0 8px 32px 0 rgba(31, 135, 62, 0.15);
+ backdrop-filter: blur(15px);
+ -webkit-backdrop-filter: blur(15px);
+}
+
+.nav-link {
+ position: relative;
+ text-decoration: none;
+ color: inherit;
+ font-weight: 400;
+ opacity: 0.6;
+ transition: opacity 0.3s ease, transform 0.2s ease;
+}
+
+.nav-link:hover {
+ opacity: 1;
+ transform: translateY(-1px);
+}
+
+.active-link {
+ opacity: 1;
+}
+
+.active-link::before {
+ content: '';
+ position: absolute;
+ width: 6px;
+ height: 6px;
+ background-color: #fff;
+ border-radius: 50%;
+ left: -12px;
+ top: 50%;
+ transform: translateY(-50%);
+ z-index: 2;
+}
+
+.cta-button {
+ font-weight: 500;
+ padding: 0 0 0 1.4rem;
+ height: calc(60px - 2px);
+ background: linear-gradient(135deg,
+ rgb(30, 160, 63),
+ rgba(24, 47, 255, 0.6));
+ background-size: 200% 200%;
+ backdrop-filter: blur(25px);
+ -webkit-backdrop-filter: blur(25px);
+ color: #fff;
+ border: none;
+ border-radius: 50px;
+ cursor: pointer;
+
+ display: flex;
+ align-items: center;
+ white-space: nowrap;
+ justify-content: space-between;
+ transition: .3s ease;
+}
+
+.cta-button span {
+ background-color: #0e0e0e;
+ margin-left: 1em;
+ margin-right: calc(1em - 8px);
+ padding-top: .1em;
+ height: 45px;
+ border-radius: 50px;
+ width: 100px;
+ font-weight: 600;
+
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.cta-button span img {
+ margin-right: 6px;
+ width: 16px;
+ height: 16px;
+ transition: .3s ease;
+}
+
+.cta-button:hover {
+ transition: .3s ease;
+}
+
+.cta-button:hover span img {
+ transform: scale(1.2);
+ transition: .3s ease;
+}
+
+@media (max-width: 900px) {
+ .header {
+ padding: 0 2em;
+ height: 120px;
+ }
+
+ .logo svg {
+ height: 30px;
+ }
+
+ .logo::before {
+ width: 180px;
+ height: 120px;
+ }
+
+ .cta-button {
+ padding: 0 0 0 1rem;
+ height: 50px;
+ font-size: 0.9rem;
+ }
+
+ .cta-button span {
+ height: 38px;
+ width: 80px;
+ margin-left: 0.8em;
+ margin-right: calc(0.8em - 6px);
+ }
+
+ .cta-button span img {
+ width: 14px;
+ height: 14px;
+ margin-right: 4px;
+ }
+}
+
+@media (max-width: 640px) {
+ .header {
+ padding: 0 1.5em;
+ height: 100px;
+ }
+
+ .logo svg {
+ height: 28px;
+ }
+
+ .logo::before {
+ width: 160px;
+ height: 110px;
+ }
+
+ .cta-button {
+ padding: 0 0 0 0.8rem;
+ height: 45px;
+ font-size: 0.85rem;
+ }
+
+ .cta-button span {
+ height: 35px;
+ width: 60px !important;
+ margin-left: 0.6em;
+ margin-right: calc(0.6em - 4px);
+ }
+
+ .cta-button span img {
+ width: 12px;
+ height: 12px;
+ margin-right: 3px;
+ }
+}
+
+@media (max-width: 480px) {
+ .header {
+ padding: 0 1rem;
+ height: 90px;
+ }
+
+ .logo svg {
+ height: 26px;
+ }
+
+ .logo::before {
+ width: 140px;
+ height: 95px;
+ }
+
+ .cta-button {
+ padding: 0 0 0 0.6rem;
+ height: 40px;
+ font-size: 0.8rem;
+ }
+
+ .cta-button span {
+ height: 32px;
+ width: 60px;
+ margin-left: 0.5em;
+ margin-right: calc(0.5em - 3px);
+ font-weight: 500;
+ }
+
+ .cta-button span img {
+ width: 10px;
+ height: 10px;
+ margin-right: 2px;
+ }
+}
+
+@media (max-width: 375px) {
+ .header {
+ padding: 0 0.8rem;
+ height: 80px;
+ }
+
+ .logo svg {
+ height: 22px;
+ }
+
+ .logo::before {
+ width: 120px;
+ height: 80px;
+ }
+
+ .cta-button {
+ padding: 0 0 0 0.5rem;
+ height: 36px;
+ font-size: 0.75rem;
+ }
+
+ .cta-button span {
+ height: 28px;
+ width: 50px;
+ margin-left: 0.4em;
+ margin-right: calc(0.4em - 2px);
+ }
+
+ .cta-button span img {
+ width: 9px;
+ height: 9px;
+ margin-right: 1px;
+ }
+}
+
+@media (min-width: 900px) {
+ .landing-nav-items {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 2rem;
+ }
+
+ .nav-cta-group {
+ gap: 2rem;
+ }
+}
\ No newline at end of file
diff --git a/src/components/landing/DisplayHeader/DisplayHeader.vue b/src/components/landing/DisplayHeader/DisplayHeader.vue
new file mode 100644
index 0000000..59f5052
--- /dev/null
+++ b/src/components/landing/DisplayHeader/DisplayHeader.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
diff --git a/src/components/landing/FeatureCards/FeatureCards.css b/src/components/landing/FeatureCards/FeatureCards.css
new file mode 100644
index 0000000..32722f2
--- /dev/null
+++ b/src/components/landing/FeatureCards/FeatureCards.css
@@ -0,0 +1,662 @@
+.features-section {
+ position: relative;
+ margin-top: 12em;
+ padding: 8rem 2rem 4em;
+ padding-bottom: 0 !important;
+ z-index: 22;
+ user-select: none;
+}
+
+.features-container {
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+.features-header {
+ text-align: center;
+ margin-bottom: 4rem;
+}
+
+.features-title {
+ font-size: 4rem;
+ font-weight: 600;
+ letter-spacing: -2px;
+ color: #fff;
+ margin-bottom: .2rem;
+ background: linear-gradient(135deg, #fff 0%, #60fa89 20%, #55f788 40%, #00ff62 60%, #55f799 80%, #fff 100%);
+ background-size: 200% 200%;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ text-align: center;
+ animation: gradientShift 4s ease-in-out infinite;
+ display: inline-block;
+ white-space: nowrap;
+}
+
+.features-title * {
+ background: inherit;
+ background-size: inherit;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ animation: inherit;
+}
+
+@keyframes gradientShift {
+
+ 0%,
+ 100% {
+ background-position: 0% 50%;
+ }
+
+ 50% {
+ background-position: 100% 50%;
+ }
+}
+
+.features-subtitle {
+ font-size: 1.2rem;
+ color: #fff;
+ text-shadow:
+ 0 0 2px rgba(255, 255, 255, 0.1),
+ 0 0 4px rgba(255, 255, 255, 0.3),
+ 0 0 8px rgba(255, 255, 255, 0.4),
+ 0 0 136px rgba(0, 255, 98, 0.9);
+ font-weight: 400;
+ margin: 0;
+ text-align: center;
+}
+
+@media (max-width: 768px) {
+ .features-title {
+ font-size: 2.5rem;
+ }
+
+ .features-subtitle {
+ font-size: 1.1rem;
+ }
+
+ .features-header {
+ margin-bottom: 3rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .features-title {
+ font-size: 2rem;
+ }
+
+ .features-subtitle {
+ font-size: 1rem;
+ }
+}
+
+.bento-grid {
+ max-width: 1200px;
+ display: grid;
+ gap: 1.5em;
+ grid-template-columns: 1fr;
+ grid-auto-rows: auto;
+}
+
+@media (min-width: 480px) and (max-width: 767px) {
+ .bento-grid {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 1.25em;
+ }
+
+ .card1 {
+ grid-column: 1 / 3;
+ grid-row: 1 / 2;
+ }
+
+ .card2 {
+ grid-column: 1 / 2;
+ grid-row: 2 / 3;
+ }
+
+ .card4 {
+ grid-column: 2 / 3;
+ grid-row: 2 / 3;
+ }
+
+
+}
+
+@media (min-width: 50rem) {
+ .bento-grid {
+ grid-template-columns: repeat(4, 1fr);
+ grid-template-rows: repeat(3, auto);
+ gap: 1.5em;
+ }
+
+ .card1 {
+ grid-column: 1 / 3;
+ grid-row: 1 / 2;
+ }
+
+ .card2 {
+ grid-column: 3 / 5;
+ grid-row: 1 / 3;
+ }
+
+ .card4 {
+ grid-column: 1 / 3;
+ grid-row: 2 / 3;
+ }
+
+
+}
+
+@media (min-width: 768px) and (max-width: 49.99rem) {
+ .bento-grid {
+ grid-template-columns: repeat(3, 1fr);
+ gap: 1.25em;
+ }
+
+ .card1 {
+ grid-column: 1 / 3;
+ grid-row: 1 / 2;
+ }
+
+ .card2 {
+ grid-column: 3 / 4;
+ grid-row: 1 / 3;
+ }
+
+ .card4 {
+ grid-column: 1 / 2;
+ grid-row: 2 / 3;
+ }
+
+
+}
+
+.feature-card {
+ user-select: none;
+ background: #0e0e0e;
+ border: 1px solid rgba(148, 184, 154, 0.2);
+ border-radius: 16px;
+ padding: 2rem;
+ text-align: left;
+ text-decoration: none;
+ transition: all 0.3s ease;
+ backdrop-filter: blur(10px);
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ justify-content: flex-end;
+ height: 100%;
+ min-height: 220px;
+ --glow-x: 50%;
+ --glow-y: 50%;
+ --glow-intensity: 0;
+}
+
+.feature-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 1px;
+ background: linear-gradient(90deg, transparent, rgba(163, 148, 184, 0.3), transparent);
+ transition: opacity 0.3s ease;
+ opacity: 0;
+}
+
+.feature-card::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ padding: 1px;
+ background: radial-gradient(200px circle at var(--glow-x) var(--glow-y),
+ rgba(132, 0, 255, calc(var(--glow-intensity) * 0.8)) 0%,
+ rgba(132, 0, 255, calc(var(--glow-intensity) * 0.4)) 30%,
+ transparent 60%);
+ border-radius: inherit;
+ mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
+ mask-composite: subtract;
+ -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
+ -webkit-mask-composite: xor;
+ pointer-events: none;
+ transition: opacity 0.3s ease;
+}
+
+.feature-card:hover {
+ box-shadow: 0 4px 40px -15px rgba(46, 24, 78, 0.4) !important;
+ background: #07160a;
+}
+
+
+
+.feature-card:hover::before {
+ opacity: 1;
+}
+
+.feature-icon {
+ font-size: 2.5rem;
+ margin-bottom: 2rem;
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ flex-shrink: 0;
+}
+
+.feature-card h3 {
+ font-size: 1rem;
+ font-weight: 600;
+ color: #fff;
+ margin-bottom: 0.5rem;
+ letter-spacing: -0.01em;
+ flex-shrink: 0;
+ position: relative;
+ z-index: 10;
+}
+
+.feature-card h2 {
+ font-size: 6rem;
+ position: relative;
+ top: 22px;
+ margin: 0;
+ font-weight: 800;
+ background: linear-gradient(135deg, #fff 0%, #60fa7f 20%, #55f783 40%, #00ff48 60%, #58f755 80%, #fff 100%);
+ background-size: 200% 200%;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ animation: gradientShift 4s ease-in-out infinite;
+ z-index: 10;
+}
+
+.feature-card p {
+ color: rgba(161, 148, 184, 0.9);
+ line-height: 1.4;
+ font-size: 0.95rem;
+ text-align: left;
+ max-width: 100%;
+ position: relative;
+ z-index: 10;
+}
+
+.particle-container {
+ position: relative;
+ overflow: hidden;
+}
+
+.particle {
+ position: absolute;
+ width: 4px;
+ height: 4px;
+ background: rgba(0, 255, 98, 0.8);
+ border-radius: 50%;
+ pointer-events: none;
+ z-index: 100;
+ box-shadow: 0 0 6px rgba(0, 255, 68, 0.6);
+}
+
+.particle::before {
+ content: '';
+ position: absolute;
+ top: -2px;
+ left: -2px;
+ right: -2px;
+ bottom: -2px;
+ background: rgba(0, 255, 81, 0.3);
+ border-radius: 50%;
+ z-index: -1;
+}
+
+.feature-card.particle-container {
+ overflow: hidden;
+}
+
+.feature-card.particle-container:hover {
+ box-shadow: 0 4px 20px rgba(24, 78, 42, 0.4), 0 0 30px rgba(0, 255, 76, 0.2);
+ background: #07160b;
+}
+
+.global-spotlight {
+ mix-blend-mode: screen;
+ will-change: transform, opacity;
+ z-index: 200 !important;
+ pointer-events: none;
+}
+
+.components-gif-wrapper {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ left: 0;
+ top: 0;
+ z-index: 1;
+ overflow: hidden;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+}
+
+.feature-card:hover .components-gif-wrapper {
+ opacity: 1;
+}
+
+.components-gif {
+ width: 100%;
+ height: auto;
+ opacity: 0.2;
+ mix-blend-mode: lighten;
+ display: block;
+ filter: grayscale(100%);
+}
+
+.components-gif-wrapper::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ height: 20%;
+ background: linear-gradient(to top, #0e0e0e 0%, transparent 100%);
+ border-radius: 0 0 8px 8px;
+ pointer-events: none;
+ z-index: 2;
+}
+
+.components-gif-wrapper::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 20%;
+ background: linear-gradient(to bottom, #0e0e0e 0%, transparent 100%);
+ border-radius: 8px 8px 0 0;
+ pointer-events: none;
+ z-index: 2;
+}
+
+.messages-gif-wrapper {
+ position: absolute;
+ mix-blend-mode: lighten;
+ top: 0.5rem;
+ right: 1rem;
+ bottom: 1rem;
+ width: 50%;
+ border-radius: 8px;
+ z-index: 1;
+ overflow: hidden;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+}
+
+.feature-card:hover .messages-gif-wrapper {
+ opacity: 1;
+}
+
+.messages-gif {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ border-radius: 0;
+ opacity: 0.3;
+ display: block;
+}
+
+.messages-gif-wrapper::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ height: 20%;
+ background: linear-gradient(to top, #0e0e0e 0%, transparent 100%);
+ border-radius: 0 0 8px 8px;
+ pointer-events: none;
+ z-index: 2;
+}
+
+.messages-gif-wrapper::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 20%;
+ background: linear-gradient(to bottom, #0e0e0e 0%, transparent 100%);
+ border-radius: 8px 8px 0 0;
+ pointer-events: none;
+ z-index: 2;
+}
+
+.switch-gif-wrapper {
+ position: absolute;
+ top: 0;
+ right: 1rem;
+ width: 40%;
+ mix-blend-mode: lighten;
+ border-radius: 8px;
+ z-index: 1;
+ overflow: hidden;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+}
+
+.feature-card:hover .switch-gif-wrapper {
+ opacity: 1;
+}
+
+.switch-gif {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ border-radius: 0;
+ opacity: 0.3;
+ display: block;
+}
+
+.switch-gif-wrapper::after {
+ content: '';
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ height: 20%;
+ background: linear-gradient(to top, #0e0e0e 0%, transparent 100%);
+ border-radius: 0 0 8px 8px;
+ pointer-events: none;
+ z-index: 2;
+}
+
+.switch-gif-wrapper::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 20%;
+ background: linear-gradient(to bottom, #0e0e0e 0%, transparent 100%);
+ border-radius: 8px 8px 0 0;
+ pointer-events: none;
+ z-index: 2;
+}
+
+
+
+@media (max-width: 479px) {
+ .features-section {
+ padding: 4rem 1rem 2rem;
+ padding-bottom: 0;
+ margin-top: 4em;
+ }
+
+ .bento-grid {
+ gap: 1rem;
+ max-width: 100%;
+ }
+
+ .feature-card {
+ min-height: 160px;
+ padding: 1.25rem;
+ border-radius: 12px;
+ }
+
+ .feature-icon {
+ font-size: 1.75rem;
+ margin-bottom: 0.75rem;
+ }
+
+ .feature-card h2 {
+ font-size: 4rem;
+ }
+
+
+
+ .feature-card h3 {
+ font-size: 1rem;
+ margin-bottom: 0.5rem;
+ }
+
+ .feature-card p {
+ font-size: 0.85rem;
+ line-height: 1.6;
+ }
+
+ .components-gif-wrapper,
+ .messages-gif-wrapper,
+ .switch-gif-wrapper {
+ display: none !important;
+ }
+}
+
+@media (max-width: 360px) {
+ .features-section {
+ padding: 2.5rem 0.5rem 1rem;
+ }
+
+ .bento-grid {
+ gap: 0.75rem;
+ }
+
+ .feature-card {
+ min-height: 150px;
+ padding: 1rem;
+ border-radius: 10px;
+ }
+
+ .feature-icon {
+ font-size: 1.5rem;
+ margin-bottom: 0.5rem;
+ }
+
+ .feature-card h2 {
+ font-size: 4rem;
+ }
+
+
+
+ .feature-card h3 {
+ font-size: 0.95rem;
+ margin-bottom: 0.4rem;
+ }
+
+ .feature-card p {
+ font-size: 0.8rem;
+ line-height: 1.7;
+ }
+
+ .components-gif-wrapper,
+ .messages-gif-wrapper,
+ .switch-gif-wrapper {
+ display: none !important;
+ }
+}
+
+@media (min-width: 768px) {
+ .features-section {
+ padding: 6rem 2rem 3rem;
+ margin-top: 6em;
+ }
+
+ .feature-card {
+ min-height: 200px;
+ padding: 1.75rem;
+ }
+
+ .feature-icon {
+ font-size: 2.25rem;
+ margin-bottom: 1.5rem;
+ }
+
+ .feature-card h3 {
+ font-size: 1.15rem;
+ }
+
+ .feature-card p {
+ font-size: 0.92rem;
+ }
+
+ .components-gif-wrapper,
+ .messages-gif-wrapper,
+ .switch-gif-wrapper {
+ display: none !important;
+ }
+}
+
+@media (min-width: 50rem) {
+ .features-section {
+ padding: 8rem 2rem 4rem;
+ padding-bottom: 0;
+ margin-top: 8em;
+ }
+
+ .feature-card {
+ min-height: 220px;
+ padding: 2rem;
+ }
+
+ .feature-icon {
+ font-size: 2.5rem;
+ margin-bottom: 1rem;
+ }
+
+ .feature-card h3 {
+ font-size: 2rem;
+ }
+
+ .feature-card p {
+ font-size: 0.95rem;
+ }
+
+ .components-gif-wrapper,
+ .messages-gif-wrapper,
+ .switch-gif-wrapper {
+ display: block !important;
+ }
+}
+
+@media (max-height: 500px) and (orientation: landscape) {
+ .features-section {
+ margin-top: 2em;
+ padding: 2rem 1rem;
+ }
+
+ .feature-card {
+ min-height: 140px;
+ padding: 1rem;
+ }
+
+ .feature-icon {
+ font-size: 1.5rem;
+ margin-bottom: 0.5rem;
+ }
+
+ .feature-card h3 {
+ font-size: 0.9rem;
+ margin-bottom: 0.3rem;
+ }
+
+ .feature-card p {
+ font-size: 0.8rem;
+ }
+}
\ No newline at end of file
diff --git a/src/components/landing/FeatureCards/FeatureCards.vue b/src/components/landing/FeatureCards/FeatureCards.vue
new file mode 100644
index 0000000..7fe705e
--- /dev/null
+++ b/src/components/landing/FeatureCards/FeatureCards.vue
@@ -0,0 +1,286 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 100
+ %
+
+ Free & Open Source
+ Loved by developers around the world
+
+
+
+
+
+
+
+ 80
+ +
+
+ Curated Components
+ Growing weekly & only getting better
+
+
+
+
+ Modern
+
+ Technologies
+ TypeScript + Tailwind, ready to ship
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/landing/Footer/Footer.css b/src/components/landing/Footer/Footer.css
new file mode 100644
index 0000000..e078525
--- /dev/null
+++ b/src/components/landing/Footer/Footer.css
@@ -0,0 +1,155 @@
+.landing-footer {
+ position: relative;
+ margin-top: 8rem;
+ padding: 2.4rem;
+ border-top: 1px solid rgba(149, 184, 148, 0.1);
+ background: linear-gradient(to bottom, transparent, #0e0e0e);
+ z-index: 220;
+}
+
+.footer-content {
+ max-width: 1200px;
+ margin: 0 auto;
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ gap: 2rem;
+}
+
+.footer-left {
+ flex-shrink: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ text-align: left;
+ gap: 0.5rem;
+}
+
+.footer-logo {
+ height: 28px;
+ width: auto;
+ opacity: 0.9;
+ transition: opacity 0.2s ease;
+ margin-bottom: 0.5rem;
+}
+
+.footer-logo:hover {
+ opacity: 1;
+}
+
+.footer-description {
+ font-size: 1rem;
+ color: rgba(161, 148, 184, 0.9);
+ margin: 0;
+ font-weight: 400;
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+}
+
+.footer-heart {
+ color: #27FF64;
+ font-size: 1em;
+ display: inline-block;
+}
+
+.footer-creator-link {
+ color: #27FF64;
+ text-decoration: none;
+ transition: color 0.2s ease;
+}
+
+.footer-creator-link:hover {
+ color: #ffffff;
+ text-decoration: underline;
+}
+
+.footer-copyright {
+ font-size: 0.85rem;
+ color: rgba(161, 148, 184, 0.7);
+ margin: 0;
+ font-weight: 400;
+}
+
+.footer-links {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-end;
+ align-items: flex-start;
+ gap: 1.25rem;
+}
+
+.footer-link {
+ font-size: 0.9rem;
+ color: rgba(255, 255, 255, 0.7);
+ text-decoration: none;
+ font-weight: 500;
+ transition: color 0.2s ease;
+ position: relative;
+ padding: 0.75rem 1rem;
+}
+
+.footer-link:hover {
+ color: #ffffff;
+}
+
+@media (max-width: 768px) {
+ .landing-footer {
+ margin-top: 6rem;
+ padding: 3rem 1.5rem 1.5rem;
+ }
+
+ .footer-content {
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ gap: 2rem;
+ }
+
+ .footer-left {
+ align-items: center;
+ text-align: center;
+ }
+
+ .footer-links {
+ justify-content: center;
+ gap: 1.5rem;
+ }
+
+ .footer-link {
+ font-size: 0.85rem;
+ padding: 0.5rem 0.75rem;
+ text-align: center;
+ }
+}
+
+@media (max-width: 480px) {
+ .landing-footer {
+ margin-top: 4rem;
+ padding: 2rem 1rem 1rem;
+ }
+
+ .footer-content {
+ gap: 1.5rem;
+ }
+
+ .footer-links {
+ gap: 1rem;
+ flex-direction: column;
+ align-items: center;
+ }
+
+ .footer-description {
+ font-size: 0.8rem;
+ }
+
+ .footer-copyright {
+ font-size: 0.75rem;
+ }
+
+ .footer-link {
+ font-size: 0.8rem;
+ padding: 0.5rem 0.75rem;
+ text-align: center;
+ }
+}
\ No newline at end of file
diff --git a/src/components/landing/Footer/Footer.vue b/src/components/landing/Footer/Footer.vue
new file mode 100644
index 0000000..24faf30
--- /dev/null
+++ b/src/components/landing/Footer/Footer.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/landing/Hero/Hero.vue b/src/components/landing/Hero/Hero.vue
new file mode 100644
index 0000000..fc32eeb
--- /dev/null
+++ b/src/components/landing/Hero/Hero.vue
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Browse Components
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/landing/PlasmaWave/PlasmaWave.vue b/src/components/landing/PlasmaWave/PlasmaWave.vue
new file mode 100644
index 0000000..1b88875
--- /dev/null
+++ b/src/components/landing/PlasmaWave/PlasmaWave.vue
@@ -0,0 +1,335 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/landing/StartBuilding/StartBuilding.css b/src/components/landing/StartBuilding/StartBuilding.css
new file mode 100644
index 0000000..4ce1d65
--- /dev/null
+++ b/src/components/landing/StartBuilding/StartBuilding.css
@@ -0,0 +1,162 @@
+.start-building-section {
+ width: 100%;
+ padding: 80px 0;
+ background: transparent;
+ position: relative;
+ z-index: 22;
+}
+
+.start-building-container {
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 0;
+}
+
+.start-building-card {
+ width: 100%;
+ max-width: 1200px;
+ user-select: none;
+ margin: 0 auto;
+ background: linear-gradient(135deg,
+ #3aed6d,
+ rgba(24, 255, 93, 0.6));
+ background-size: 200% 200%;
+ border-radius: 16px;
+ padding: 4rem 3rem;
+ backdrop-filter: blur(10px);
+ position: relative;
+ overflow: hidden;
+ transition: all 0.3s ease;
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 1.5rem;
+}
+
+.start-building-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-image: url('/assets/grain.webp');
+ background-size: 500px 500px;
+ filter: invert(100%);
+ mix-blend-mode: multiply;
+ background-repeat: repeat;
+ opacity: 1;
+ pointer-events: none;
+ z-index: 1;
+}
+
+.start-building-card > * {
+ position: relative;
+ z-index: 2;
+}
+
+.start-building-title {
+ color: #0e0e0e;
+ font-size: 2.6rem;
+ font-weight: 600;
+ margin: 0;
+ line-height: 1;
+}
+
+.start-building-subtitle {
+ color: #0e0e0e;
+ font-size: 1.2rem;
+ font-weight: 500;
+ margin: -1rem 0 0 0;
+ opacity: 0.9;
+ max-width: 600px;
+ line-height: 1.4;
+}
+
+.start-building-button {
+ background: transparent;
+ color: #0e0e0e;
+ border: 2px solid #0e0e0e;
+ padding: .6rem 1.6rem;
+ font-size: 1.1rem;
+ font-weight: 600;
+ border-radius: 50px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.start-building-button:hover {
+ background: #0e0e0e;
+ color: #27FF64;
+}
+
+@media (max-width: 1280px) {
+ .start-building-container {
+ max-width: 1000px;
+ padding: 0 30px;
+ }
+
+ .start-building-card {
+ padding: 2.5rem;
+ }
+}
+
+@media (max-width: 768px) {
+ .start-building-section {
+ padding: 60px 0;
+ }
+
+ .start-building-container {
+ padding: 0 20px;
+ }
+
+ .start-building-card {
+ padding: 3rem 2rem;
+ border-radius: 12px;
+ gap: 1.25rem;
+ }
+
+ .start-building-title {
+ font-size: 2rem;
+ }
+
+ .start-building-subtitle {
+ font-size: 1.1rem;
+ }
+
+ .start-building-button {
+ padding: 0.875rem 1.75rem;
+ font-size: 1rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .start-building-section {
+ padding: 40px 0;
+ }
+
+ .start-building-container {
+ padding: 0 16px;
+ }
+
+ .start-building-card {
+ padding: 2.5rem 1.5rem;
+ gap: 1rem;
+ }
+
+ .start-building-title {
+ font-size: 1.5rem;
+ }
+
+ .start-building-subtitle {
+ font-size: 1rem;
+ margin: 0;
+ padding: 0 0.6rem;
+ }
+
+ .start-building-button {
+ padding: 0.75rem 1.5rem;
+ font-size: 0.95rem;
+ }
+}
\ No newline at end of file
diff --git a/src/components/landing/StartBuilding/StartBuilding.vue b/src/components/landing/StartBuilding/StartBuilding.vue
new file mode 100644
index 0000000..5d881ab
--- /dev/null
+++ b/src/components/landing/StartBuilding/StartBuilding.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
Start exploring Vue Bits
+
Animations, components, backgrounds - it's all here
+
+
+ Browse Components
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/components/layouts/CategoryLayout.vue b/src/components/layouts/CategoryLayout.vue
new file mode 100644
index 0000000..7faf18b
--- /dev/null
+++ b/src/components/layouts/CategoryLayout.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/src/composables/useStars.ts b/src/composables/useStars.ts
new file mode 100644
index 0000000..c7500f2
--- /dev/null
+++ b/src/composables/useStars.ts
@@ -0,0 +1,49 @@
+import { ref, onMounted } from 'vue'
+import { getStarsCount } from '@/utils/utils'
+
+const CACHE_KEY = 'github_stars_cache'
+const CACHE_DURATION = 24 * 60 * 60 * 1000 // 24 hours
+
+export function useStars() {
+ const stars = ref(0)
+
+ const fetchStars = async () => {
+ try {
+ const cachedData = localStorage.getItem(CACHE_KEY)
+
+ if (cachedData) {
+ const { count, timestamp } = JSON.parse(cachedData)
+ const now = Date.now()
+
+ if (now - timestamp < CACHE_DURATION) {
+ stars.value = count
+ return
+ }
+ }
+
+ const count = await getStarsCount()
+
+ localStorage.setItem(CACHE_KEY, JSON.stringify({
+ count,
+ timestamp: Date.now()
+ }))
+
+ stars.value = count
+ } catch (error) {
+ console.error('Error fetching stars:', error)
+
+ // Fall back to cached data if available
+ const cachedData = localStorage.getItem(CACHE_KEY)
+ if (cachedData) {
+ const { count } = JSON.parse(cachedData)
+ stars.value = count
+ }
+ }
+ }
+
+ onMounted(() => {
+ fetchStars()
+ })
+
+ return stars
+}
diff --git a/src/content/Animations/CountUp/CountUp.vue b/src/content/Animations/CountUp/CountUp.vue
new file mode 100644
index 0000000..2c8717a
--- /dev/null
+++ b/src/content/Animations/CountUp/CountUp.vue
@@ -0,0 +1,163 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/content/Animations/FadeContent/FadeContent.vue b/src/content/Animations/FadeContent/FadeContent.vue
new file mode 100644
index 0000000..1279e3c
--- /dev/null
+++ b/src/content/Animations/FadeContent/FadeContent.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/content/Backgrounds/DotGrid/DotGrid.vue b/src/content/Backgrounds/DotGrid/DotGrid.vue
new file mode 100644
index 0000000..3e6512d
--- /dev/null
+++ b/src/content/Backgrounds/DotGrid/DotGrid.vue
@@ -0,0 +1,315 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/content/Backgrounds/LetterGlitch/LetterGlitch.vue b/src/content/Backgrounds/LetterGlitch/LetterGlitch.vue
new file mode 100644
index 0000000..2005c1e
--- /dev/null
+++ b/src/content/Backgrounds/LetterGlitch/LetterGlitch.vue
@@ -0,0 +1,246 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/content/Backgrounds/Squares/Squares.vue b/src/content/Backgrounds/Squares/Squares.vue
new file mode 100644
index 0000000..6280463
--- /dev/null
+++ b/src/content/Backgrounds/Squares/Squares.vue
@@ -0,0 +1,197 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src/content/TextAnimations/SplitText/SplitText.vue b/src/content/TextAnimations/SplitText/SplitText.vue
new file mode 100644
index 0000000..8f2f389
--- /dev/null
+++ b/src/content/TextAnimations/SplitText/SplitText.vue
@@ -0,0 +1,192 @@
+
+
+ {{ text }}
+
+
+
+
+
diff --git a/src/css/base.css b/src/css/base.css
index 9f43709..eaf4da4 100644
--- a/src/css/base.css
+++ b/src/css/base.css
@@ -1,4 +1,5 @@
@import 'tailwindcss';
+@import 'primeicons/primeicons.css';
*,
*::before,
@@ -7,6 +8,8 @@
}
body {
+ background-color: #0e0e0e;
+ color: #fff;
min-height: 100vh;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
diff --git a/src/css/landing.css b/src/css/landing.css
new file mode 100644
index 0000000..f38ea74
--- /dev/null
+++ b/src/css/landing.css
@@ -0,0 +1,922 @@
+.landing-wrapper {
+ min-height: 100dvh;
+ position: relative;
+ overflow-x: hidden;
+}
+
+.landing-wrapper::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 300px;
+ height: 100vh;
+ background: linear-gradient(to right, #0e0e0e, transparent);
+ z-index: 2;
+ pointer-events: none;
+}
+
+.landing-wrapper::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 300px;
+ height: 100vh;
+ background: linear-gradient(to left, #0e0e0e, transparent);
+ z-index: 2;
+ pointer-events: none;
+}
+
+.landing-content {
+ position: relative;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: space-between;
+ height: 100%;
+ max-width: calc(1200px + 6em);
+ margin: 0 auto;
+ padding: 0 4em;
+ margin-top: 250px;
+ z-index: 10;
+}
+
+.landing-title {
+ user-select: none;
+ white-space: nowrap !important;
+ font-size: 3.6rem;
+ font-weight: 500;
+ position: relative;
+ z-index: 6;
+ color: #fff;
+ margin-bottom: 1rem;
+ max-width: 22ch;
+ letter-spacing: -3px;
+ line-height: 1;
+ text-shadow:
+ 0 0 2px rgba(255, 255, 255, 0.1),
+ 0 0 4px rgba(255, 255, 255, 0.3),
+ 0 0 8px rgba(255, 255, 255, 0.4),
+ 0 0 136px rgba(60, 255, 79, 0.4);
+}
+
+.landing-subtitle {
+ user-select: none;
+ overflow: visible !important;
+ font-size: 1.2rem;
+ text-align: left !important;
+ font-weight: 300;
+ line-height: 1.2;
+ color: #A7EF9E;
+ max-width: 30ch;
+ margin-bottom: 2rem;
+ text-shadow:
+ 0 0 2px rgba(255, 255, 255, 0.1),
+ 0 0 4px rgba(255, 255, 255, 0.3),
+ 0 0 8px rgba(255, 255, 255, 0.4),
+ 0 0 136px rgba(60, 255, 79, 0.8);
+ z-index: 6;
+}
+
+.hero-split {
+ white-space: nowrap !important;
+ overflow: visible !important;
+}
+
+.landing-button {
+ position: relative;
+ background: linear-gradient(135deg,
+ rgb(30, 160, 63),
+ rgba(24, 47, 255, 0.6));
+ background-size: 200% 200%;
+ font-weight: 600;
+ font-size: 1.1rem;
+ border-radius: 50px;
+ border: none;
+ padding: 1rem 1rem 1rem 2.5rem;
+ color: white;
+ cursor: pointer;
+ isolation: isolate;
+ z-index: 15;
+ box-shadow:
+ 0 0 40px rgba(58, 237, 112, 0.4),
+ 0 0 80px rgba(92, 246, 138, 0.3),
+ 0 8px 32px rgba(0, 0, 0, 0.3);
+ transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ animation: glow-pulse 3s ease-in-out infinite alternate;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
+ letter-spacing: 0.5px;
+ overflow: hidden;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1.6rem;
+}
+
+.button-arrow-circle {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 38px;
+ height: 38px;
+ background: rgba(255, 255, 255, 0.9);
+ border-radius: 50%;
+ color: #1d9559;
+ transition: all 0.3s ease;
+ backdrop-filter: blur(10px);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ flex-shrink: 0;
+}
+
+.landing-button:hover .button-arrow-circle {
+ background: rgba(255, 255, 255, 1);
+ transform: translateX(4px);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+}
+
+.landing-button span {
+ position: relative;
+ z-index: 2;
+ display: inline-block;
+ transition: all 0.3s ease;
+}
+
+.landing-button:hover span {
+ transform: scale(1.01);
+}
+
+.landing-button::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: -100%;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(90deg,
+ transparent,
+ rgba(255, 255, 255, 0.4),
+ transparent);
+ transition: left 0.6s ease;
+ z-index: 1;
+}
+
+.landing-button::after {
+ content: '';
+ position: absolute;
+ inset: -2px;
+ background: linear-gradient(45deg,
+ transparent,
+ rgba(255, 255, 255, 0.1),
+ transparent,
+ rgba(255, 255, 255, 0.1),
+ transparent);
+ background-size: 200% 200%;
+ border-radius: 50px;
+ z-index: -1;
+ animation: border-dance 4s linear infinite;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+}
+
+.landing-button:hover {
+ box-shadow:
+ 0 0 60px rgba(58, 237, 109, 0.2),
+ 0 0 120px rgba(92, 246, 138, 0.2),
+ 0 0 180px rgba(40, 217, 90, 0.2),
+ 0 12px 40px rgba(0, 0, 0, 0.4),
+ inset 0 2px 0 rgba(255, 255, 255, 0.4),
+ inset 0 -2px 0 rgba(0, 0, 0, 0.3);
+ transform: translateY(-4px) scale(1.01);
+ transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
+ animation-duration: 1.5s;
+}
+
+.landing-button:hover::before {
+ left: 100%;
+}
+
+.landing-button:hover::after {
+ opacity: 1;
+}
+
+.landing-button:active {
+ transform: translateY(-2px) scale(1.02);
+ transition: all 0.1s ease;
+}
+
+@keyframes glow-pulse {
+ 0% {
+ filter: brightness(1) saturate(1);
+ }
+
+ 100% {
+ filter: brightness(1.1) saturate(1.2);
+ }
+}
+
+@keyframes border-dance {
+ 0% {
+ background-position: 0% 50%;
+ }
+
+ 50% {
+ background-position: 100% 50%;
+ }
+
+ 100% {
+ background-position: 0% 50%;
+ }
+}
+
+@keyframes fadeInUp {
+ 0% {
+ opacity: 0;
+ transform: translateY(30px) scale(0.9);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+}
+
+@keyframes fadeInUpRotate1 {
+ 0% {
+ opacity: 0;
+ transform: translateY(30px) scale(0.9) rotate(-13deg);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: translateY(0) scale(1) rotate(-13deg);
+ }
+}
+
+@keyframes fadeInUpRotate2 {
+ 0% {
+ opacity: 0;
+ transform: translateY(30px) scale(0.9) rotate(10deg);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: translateY(0) scale(1) rotate(10deg);
+ }
+}
+
+@keyframes fadeInUpRotate3 {
+ 0% {
+ opacity: 0;
+ transform: translateY(30px) scale(0.9) rotate(-5deg);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: translateY(0) scale(1) rotate(-5deg);
+ }
+}
+
+@keyframes fadeInUpMobile {
+ 0% {
+ opacity: 0;
+ transform: translateY(30px) scale(0.9);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+}
+
+@keyframes fadeInUpMobileRotate1 {
+ 0% {
+ opacity: 0;
+ transform: translateY(30px) scale(0.9) rotate(-13deg);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: translateY(0) scale(1) rotate(-13deg);
+ }
+}
+
+@keyframes fadeInUpMobileRotate2 {
+ 0% {
+ opacity: 0;
+ transform: translateY(30px) scale(0.9) rotate(10deg);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: translateY(0) scale(1) rotate(10deg);
+ }
+}
+
+@keyframes fadeInUpMobileRotate3 {
+ 0% {
+ opacity: 0;
+ transform: translateY(30px) scale(0.9) rotate(-5deg);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: translateY(0) scale(1) rotate(-5deg);
+ }
+}
+
+.hero-main-content {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ max-width: 60%;
+}
+
+.hero-cards-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 2.4rem;
+ max-width: 40%;
+ margin-left: .6rem;
+}
+
+.hero-cards-row {
+ display: flex;
+ gap: 2.4rem;
+}
+
+.hero-glitch {
+ mix-blend-mode: screen;
+ border-radius: 30px !important;
+}
+
+.hero-dot-grid {
+ overflow: hidden !important;
+ transform: translateY(5px) translateX(5px);
+}
+
+.hero-metaballs {
+ mix-blend-mode: screen;
+ opacity: 1;
+}
+
+.hero-falling-text .falling-text-canvas {
+ width: 100% !important;
+ height: 100% !important;
+}
+
+.hero-lines {
+ width: 100% !important;
+ height: 100% !important;
+}
+
+.hero-card {
+ width: 200px;
+ height: 200px;
+ background: linear-gradient(135deg, rgba(58, 237, 109, 0.2), rgba(40, 217, 72, 0.2));
+ border: 2px solid rgba(255, 255, 255, 0.2);
+ border-radius: 30px;
+ box-shadow:
+ 0 30px 100px rgba(0, 16, 5, 0.95),
+ 0 20px 70px rgba(0, 16, 5, 0.9),
+ 0 15px 50px rgba(0, 16, 5, 0.8),
+ 0 10px 30px rgba(0, 16, 7, 0.7),
+ 0 8px 32px rgba(58, 237, 121, 0.4),
+ inset 0 1px 0 rgba(255, 255, 255, 0.2);
+ backdrop-filter: blur(20px);
+ -webkit-backdrop-filter: blur(20px);
+ transition: all 0.3s ease;
+ position: relative;
+ overflow: hidden;
+ cursor: pointer;
+ opacity: 0;
+}
+
+.hero-card-1 {
+ animation: fadeInUpRotate1 0.8s ease-out forwards;
+ animation-delay: 0.2s;
+ transform: translateY(0) scale(1) rotate(-13deg);
+}
+
+.hero-card-2 {
+ animation: fadeInUpRotate2 0.8s ease-out forwards;
+ animation-delay: 0.4s;
+ transform: translateY(0) scale(1) rotate(10deg);
+}
+
+.hero-card-3 {
+ animation: fadeInUpRotate3 0.8s ease-out forwards;
+ animation-delay: 0.6s;
+ transform: translateY(0) scale(1) rotate(-5deg);
+}
+
+.hero-card:hover {
+ filter: grayscale(50%);
+ box-shadow:
+ 0 50px 140px rgba(0, 16, 5, 0.98),
+ 0 35px 100px rgba(0, 16, 4, 0.95),
+ 0 25px 70px rgba(0, 16, 4, 0.9),
+ 0 15px 50px rgba(0, 16, 4, 0.8),
+ 0 12px 40px rgba(58, 237, 115, 0.6),
+ inset 0 1px 0 rgba(255, 255, 255, 0.3);
+ backdrop-filter: blur(25px);
+ -webkit-backdrop-filter: blur(25px);
+}
+
+.hero-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: -100%;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
+ transition: left 0.5s ease;
+}
+
+.hero-card:hover::before {
+ left: 100%;
+}
+
+@media (max-width: 1440px) {
+ .landing-title {
+ font-size: 3.6rem;
+ }
+
+ .landing-subtitle {
+ font-size: 1.3rem;
+ }
+
+ .landing-content {
+ margin-top: 250px;
+ }
+}
+
+@media (max-width: 1366px) {
+ .landing-title {
+ font-size: 3.6rem;
+ }
+
+ .landing-subtitle {
+ font-size: 1.2rem;
+ }
+
+ .landing-content {
+ margin-top: 250px;
+ }
+}
+
+@media (max-width: 1200px) {
+ .landing-title {
+ font-size: 3.6rem;
+ }
+
+ .landing-subtitle {
+ font-size: 1.1rem;
+ }
+
+ .landing-content {
+ margin-top: 250px;
+ }
+}
+
+@media (max-width: 1100px) {
+ .landing-title {
+ font-size: 3.6rem;
+ }
+
+ .landing-subtitle {
+ font-size: 1rem;
+ }
+
+ .landing-content {
+ margin-top: 250px;
+ }
+}
+
+@media (max-width: 1024px) {
+ .landing-title {
+ font-size: 3.6rem;
+ }
+
+ .landing-subtitle {
+ font-size: 0.95rem;
+ }
+
+ .landing-content {
+ margin-top: 300px;
+ padding: 0 2em;
+ max-width: calc(1200px + 2em);
+ flex-direction: column;
+ align-items: center;
+ }
+
+ .hero-main-content {
+ max-width: 100%;
+ align-items: center;
+ text-align: center;
+ margin-bottom: 2rem;
+ }
+
+ .hero-cards-container {
+ max-width: 100%;
+ width: 100%;
+ margin-left: 0;
+ margin-top: 2rem;
+ flex-direction: row;
+ gap: 1.5rem;
+ justify-content: space-between;
+ }
+
+ .hero-cards-row {
+ display: contents;
+ }
+
+ .hero-card {
+ flex: 1;
+ height: 120px;
+ max-width: calc(33.333% - 1rem);
+ }
+
+ .hero-card-1 {
+ animation: fadeInUpMobileRotate1 0.8s ease-out forwards;
+ animation-delay: 0.2s;
+ transform: translateY(0) scale(1) rotate(-13deg);
+ }
+
+ .hero-card-2 {
+ animation: fadeInUpMobileRotate2 0.8s ease-out forwards;
+ animation-delay: 0.4s;
+ transform: translateY(0) scale(1) rotate(10deg);
+ }
+
+ .hero-card-3 {
+ animation: fadeInUpMobileRotate3 0.8s ease-out forwards;
+ animation-delay: 0.6s;
+ transform: translateY(0) scale(1) rotate(-5deg);
+ }
+
+ .landing-wrapper>div[style*="position: absolute"][style*="width: 100vw"][style*="height: 100vh"] {
+ opacity: 0.7;
+ }
+}
+
+@media (max-width: 950px) {
+ .landing-title {
+ font-size: 3.6rem;
+ }
+
+ .landing-subtitle {
+ font-size: 0.9rem;
+ text-align: center !important;
+ }
+
+ .landing-content {
+ margin-top: 290px;
+ }
+}
+
+@media (max-width: 900px) {
+ .landing-title {
+ font-size: 3.6rem;
+ }
+
+ .landing-subtitle {
+ font-size: 0.85rem;
+ text-align: center !important;
+ }
+
+ .landing-content {
+ margin-top: 280px;
+ }
+
+ .landing-wrapper::before {
+ width: 100px;
+ }
+
+ .landing-wrapper::after {
+ width: 100px;
+ }
+}
+
+@media (max-width: 820px) {
+ .landing-title {
+ font-size: 3.4rem;
+ }
+
+ .landing-subtitle {
+ font-size: 0.8rem;
+ }
+
+ .landing-content {
+ margin-top: 270px;
+ }
+}
+
+@media (max-width: 768px) {
+ .landing-title {
+ font-size: 3.2rem;
+ text-align: center;
+ max-width: 100%;
+ }
+
+ .landing-subtitle {
+ font-size: 0.75rem;
+ text-align: center !important;
+ max-width: 100%;
+ }
+
+ .landing-content {
+ margin-top: 260px;
+ align-items: center;
+ padding: 0 1.5rem;
+ }
+
+ .landing-button {
+ padding: 0.8rem 0.8rem 0.8rem 1.8rem;
+ font-size: 1rem;
+ gap: 1.2rem;
+ }
+
+ .button-arrow-circle {
+ width: 32px;
+ height: 32px;
+ }
+
+ .hero-cards-container {
+ flex-direction: row;
+ gap: 1.2rem;
+ width: 100%;
+ justify-content: space-between;
+ }
+
+ .hero-cards-row {
+ display: contents;
+ }
+
+ .hero-card {
+ flex: 1;
+ height: 120px;
+ max-width: calc(33.333% - 0.8rem);
+ }
+
+ .hero-card-1 {
+ animation: fadeInUpMobileRotate1 0.8s ease-out forwards;
+ animation-delay: 0.2s;
+ transform: translateY(0) scale(1) rotate(-13deg);
+ }
+
+ .hero-card-2 {
+ animation: fadeInUpMobileRotate2 0.8s ease-out forwards;
+ animation-delay: 0.4s;
+ transform: translateY(0) scale(1) rotate(10deg);
+ }
+
+ .hero-card-3 {
+ animation: fadeInUpMobileRotate3 0.8s ease-out forwards;
+ animation-delay: 0.6s;
+ transform: translateY(0) scale(1) rotate(-5deg);
+ }
+
+ .landing-wrapper>div[style*="position: absolute"][style*="width: 100vw"][style*="height: 100vh"] {
+ opacity: 0.5;
+ }
+}
+
+@media (max-width: 700px) {
+ .landing-title {
+ font-size: 2.6rem;
+ margin-bottom: 2rem;
+ text-align: center;
+ max-width: 100%;
+ }
+
+ .landing-subtitle {
+ font-size: 1rem;
+ text-align: center !important;
+ max-width: 100%;
+ }
+
+ .landing-content {
+ margin-top: 250px;
+ align-items: center;
+ padding: 0 1.5rem;
+ }
+}
+
+@media (max-width: 640px) {
+ .landing-title {
+ font-size: 2.4rem;
+ text-align: center;
+ max-width: 100%;
+ }
+
+ .landing-subtitle {
+ text-align: center !important;
+ max-width: 100%;
+ }
+
+ .landing-content {
+ margin-top: 240px;
+ align-items: center;
+ padding: 0 1.5rem;
+ }
+}
+
+@media (max-width: 580px) {
+ .landing-title {
+ font-size: 2.2rem;
+ text-align: center;
+ max-width: 100%;
+ }
+
+ .landing-subtitle {
+ text-align: center !important;
+ max-width: 100%;
+ }
+
+ .landing-content {
+ margin-top: 230px;
+ align-items: center;
+ padding: 0 1.5rem;
+ }
+}
+
+@media (max-width: 520px) {
+ .landing-title {
+ font-size: 2rem;
+ text-align: center;
+ max-width: 100%;
+ }
+
+ .landing-subtitle {
+ text-align: center !important;
+ max-width: 100%;
+ }
+
+ .landing-content {
+ margin-top: 220px;
+ align-items: center;
+ padding: 0 1.5rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .landing-title {
+ font-size: 1.8rem;
+ text-align: center;
+ max-width: 100%;
+ }
+
+ .landing-subtitle {
+ text-align: center !important;
+ max-width: 100%;
+ }
+
+ .landing-content {
+ margin-top: 210px;
+ align-items: center;
+ padding: 0 1.5rem;
+ }
+
+ .landing-button {
+ padding: 0.7rem 0.7rem 0.7rem 1.5rem;
+ font-size: 0.95rem;
+ gap: 1rem;
+ }
+
+ .button-arrow-circle {
+ width: 28px;
+ height: 28px;
+ }
+
+ .hero-cards-container {
+ flex-direction: row;
+ gap: 1rem;
+ width: 100%;
+ justify-content: space-between;
+ }
+
+ .hero-cards-row {
+ display: contents;
+ }
+
+ .hero-card {
+ flex: 1;
+ height: 120px;
+ max-width: calc(33.333% - 0.666rem);
+ }
+
+ .hero-card-1 {
+ animation: fadeInUpMobileRotate1 0.8s ease-out forwards;
+ animation-delay: 0.2s;
+ transform: translateY(0) scale(1) rotate(-13deg);
+ }
+
+ .hero-card-2 {
+ animation: fadeInUpMobileRotate2 0.8s ease-out forwards;
+ animation-delay: 0.4s;
+ transform: translateY(0) scale(1) rotate(10deg);
+ }
+
+ .hero-card-3 {
+ animation: fadeInUpMobileRotate3 0.8s ease-out forwards;
+ animation-delay: 0.6s;
+ transform: translateY(0) scale(1) rotate(-5deg);
+ }
+
+ .landing-wrapper>div[style*="position: absolute"][style*="width: 100vw"][style*="height: 100vh"] {
+ opacity: 0.3;
+ }
+}
+
+@media (max-width: 430px) {
+ .landing-title {
+ font-size: 1.6rem;
+ letter-spacing: -1px;
+ text-align: center;
+ max-width: 100%;
+ }
+
+ .landing-subtitle {
+ text-align: center !important;
+ max-width: 100%;
+ }
+
+ .landing-content {
+ margin-top: 205px;
+ align-items: center;
+ padding: 0 1.5rem;
+ }
+}
+
+@media (max-width: 390px) {
+ .landing-title {
+ font-size: 1.8rem;
+ letter-spacing: -1px;
+ text-align: center;
+ max-width: 100%;
+ }
+
+ .landing-subtitle {
+ text-align: center !important;
+ max-width: 100%;
+ }
+
+ .landing-content {
+ margin-top: 202px;
+ align-items: center;
+ padding: 0 1.5rem;
+ }
+}
+
+@media (max-width: 375px) {
+ .landing-title {
+ font-size: 1.6rem;
+ letter-spacing: -1px;
+ text-align: center;
+ max-width: 100%;
+ }
+
+ .landing-subtitle {
+ text-align: center !important;
+ max-width: 100%;
+ }
+
+ .landing-content {
+ margin-top: 150px;
+ align-items: center;
+ padding: 0 1.5rem;
+ }
+
+ .landing-button {
+ padding: 0.6rem 0.6rem 0.6rem 1.2rem;
+ font-size: 0.9rem;
+ gap: 0.8rem;
+ }
+
+ .button-arrow-circle {
+ width: 26px;
+ height: 26px;
+ }
+}
+
+/* Mobile hero background styles */
+.mobile-hero-background-container {
+ position: absolute;
+ inset: 0;
+ overflow: hidden;
+ width: 100vw;
+ height: 100vh;
+ z-index: -1;
+}
+
+.mobile-hero-background-image {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ opacity: 0.6;
+}
\ No newline at end of file
diff --git a/src/css/main.css b/src/css/main.css
index 08fbe90..3e7cf8d 100644
--- a/src/css/main.css
+++ b/src/css/main.css
@@ -1,2 +1,3 @@
@import './base.css';
+@import './landing.css';
diff --git a/src/pages/CategoryPage.vue b/src/pages/CategoryPage.vue
new file mode 100644
index 0000000..d8fe378
--- /dev/null
+++ b/src/pages/CategoryPage.vue
@@ -0,0 +1,11 @@
+
+
+
Category Page
+
Category: {{ $route.params.category }}
+
Subcategory: {{ $route.params.subcategory }}
+
+
+
+
diff --git a/src/pages/LandingPage.vue b/src/pages/LandingPage.vue
new file mode 100644
index 0000000..8b200ea
--- /dev/null
+++ b/src/pages/LandingPage.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
diff --git a/src/router/index.ts b/src/router/index.ts
index b7cb91b..5b6cd6b 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,5 +1,7 @@
import { createRouter, createWebHistory } from 'vue-router'
-import App from '@/App.vue'
+import LandingPage from '@/pages/LandingPage.vue'
+import CategoryPage from '@/pages/CategoryPage.vue'
+import CategoryLayout from '@/components/layouts/CategoryLayout.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
@@ -7,7 +9,18 @@ const router = createRouter({
{
path: '/',
name: 'home',
- component: App,
+ component: LandingPage,
+ },
+ {
+ path: '/:category/:subcategory',
+ name: 'category',
+ component: CategoryLayout,
+ children: [
+ {
+ path: '',
+ component: CategoryPage,
+ }
+ ]
}
],
})
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
new file mode 100644
index 0000000..740eca8
--- /dev/null
+++ b/src/utils/utils.ts
@@ -0,0 +1,20 @@
+/**
+ * Fetches the star count for a GitHub repository
+ * @param repo - Repository in format "owner/repo"
+ * @returns Promise - The star count
+ */
+export const getStarsCount = async (repo: string = 'DavidHDev/vue-bits'): Promise => {
+ try {
+ const response = await fetch(`https://api.github.com/repos/${repo}`)
+
+ if (!response.ok) {
+ throw new Error(`GitHub API error: ${response.status}`)
+ }
+
+ const data = await response.json()
+ return data.stargazers_count || 0
+ } catch (error) {
+ console.error('Error fetching GitHub stars:', error)
+ throw error
+ }
+}
diff --git a/tsconfig.app.json b/tsconfig.app.json
index 913b8f2..0c7dc61 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -1,12 +1,19 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
- "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
- "exclude": ["src/**/__tests__/*"],
+ "include": [
+ "env.d.ts",
+ "src/**/*",
+ "src/**/*.vue"
+ ],
+ "exclude": [
+ "src/**/__tests__/*"
+ ],
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
-
"paths": {
- "@/*": ["./src/*"]
+ "@/*": [
+ "./src/*"
+ ]
}
}
-}
+}
\ No newline at end of file