diff --git a/index.html b/index.html index 31f5b33..d496271 100644 --- a/index.html +++ b/index.html @@ -2,9 +2,8 @@ - - Jose.portfolio() + Jose::portfolio()
diff --git a/package-lock.json b/package-lock.json index daaf135..6f04a22 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,8 @@ "framer-motion": "^12.4.7", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-router-dom": "^7.2.0" + "react-router-dom": "^7.2.0", + "twemoji": "^14.0.2" }, "devDependencies": { "@eslint/js": "^9.19.0", @@ -2726,6 +2727,29 @@ } } }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-extra/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2905,6 +2929,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", @@ -3529,6 +3559,18 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-5.0.0.tgz", + "integrity": "sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==", + "license": "MIT", + "dependencies": { + "universalify": "^0.1.2" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -4823,6 +4865,24 @@ "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", "license": "ISC" }, + "node_modules/twemoji": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/twemoji/-/twemoji-14.0.2.tgz", + "integrity": "sha512-BzOoXIe1QVdmsUmZ54xbEH+8AgtOKUiG53zO5vVP2iUu6h5u9lN15NcuS6te4OY96qx0H7JK9vjjl9WQbkTRuA==", + "license": "MIT", + "dependencies": { + "fs-extra": "^8.0.1", + "jsonfile": "^5.0.0", + "twemoji-parser": "14.0.0", + "universalify": "^0.1.2" + } + }, + "node_modules/twemoji-parser": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/twemoji-parser/-/twemoji-parser-14.0.0.tgz", + "integrity": "sha512-9DUOTGLOWs0pFWnh1p6NF+C3CkQ96PWmEFwhOVmT3WbecRC+68AIqpsnJXygfkFcp4aXbOp8Dwbhh/HQgvoRxA==", + "license": "MIT" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4933,6 +4993,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", diff --git a/package.json b/package.json index f6236b0..14ea23f 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "framer-motion": "^12.4.7", "react": "^19.0.0", "react-dom": "^19.0.0", - "react-router-dom": "^7.2.0" + "react-router-dom": "^7.2.0", + "twemoji": "^14.0.2" }, "devDependencies": { "@eslint/js": "^9.19.0", diff --git a/public/binary.png b/public/binary.png deleted file mode 100755 index 0d91c2c..0000000 Binary files a/public/binary.png and /dev/null differ diff --git a/public/cat.gif b/public/cat.gif new file mode 100644 index 0000000..5d7e8f3 Binary files /dev/null and b/public/cat.gif differ diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index f897112..0000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/public/fonts/FiraCode.ttf b/public/fonts/FiraCode.ttf new file mode 100644 index 0000000..d7077f1 Binary files /dev/null and b/public/fonts/FiraCode.ttf differ diff --git a/public/fonts/OpenSans.ttf b/public/fonts/OpenSans.ttf new file mode 100644 index 0000000..ac587b4 Binary files /dev/null and b/public/fonts/OpenSans.ttf differ diff --git a/public/fonts/ProductSansBold.ttf b/public/fonts/ProductSansBold.ttf new file mode 100644 index 0000000..d847195 Binary files /dev/null and b/public/fonts/ProductSansBold.ttf differ diff --git a/public/fonts/ProductSansBoldItalic.ttf b/public/fonts/ProductSansBoldItalic.ttf new file mode 100644 index 0000000..129d12d Binary files /dev/null and b/public/fonts/ProductSansBoldItalic.ttf differ diff --git a/public/fonts/ProductSansItalic.ttf b/public/fonts/ProductSansItalic.ttf new file mode 100644 index 0000000..5fc56d4 Binary files /dev/null and b/public/fonts/ProductSansItalic.ttf differ diff --git a/public/fonts/ProductSansRegular.ttf b/public/fonts/ProductSansRegular.ttf new file mode 100644 index 0000000..c0442ee Binary files /dev/null and b/public/fonts/ProductSansRegular.ttf differ diff --git a/public/fonts/Ubuntu-Bold.ttf b/public/fonts/Ubuntu-Bold.ttf deleted file mode 100644 index c2293d5..0000000 Binary files a/public/fonts/Ubuntu-Bold.ttf and /dev/null differ diff --git a/public/fonts/Ubuntu-BoldItalic.ttf b/public/fonts/Ubuntu-BoldItalic.ttf deleted file mode 100644 index ce6e784..0000000 Binary files a/public/fonts/Ubuntu-BoldItalic.ttf and /dev/null differ diff --git a/public/fonts/Ubuntu-Italic.ttf b/public/fonts/Ubuntu-Italic.ttf deleted file mode 100644 index a599244..0000000 Binary files a/public/fonts/Ubuntu-Italic.ttf and /dev/null differ diff --git a/public/fonts/Ubuntu-Light.ttf b/public/fonts/Ubuntu-Light.ttf deleted file mode 100644 index b310d15..0000000 Binary files a/public/fonts/Ubuntu-Light.ttf and /dev/null differ diff --git a/public/fonts/Ubuntu-LightItalic.ttf b/public/fonts/Ubuntu-LightItalic.ttf deleted file mode 100644 index ad0741b..0000000 Binary files a/public/fonts/Ubuntu-LightItalic.ttf and /dev/null differ diff --git a/public/fonts/Ubuntu-Medium.ttf b/public/fonts/Ubuntu-Medium.ttf deleted file mode 100644 index 7340a40..0000000 Binary files a/public/fonts/Ubuntu-Medium.ttf and /dev/null differ diff --git a/public/fonts/Ubuntu-MediumItalic.ttf b/public/fonts/Ubuntu-MediumItalic.ttf deleted file mode 100644 index 36ac1ae..0000000 Binary files a/public/fonts/Ubuntu-MediumItalic.ttf and /dev/null differ diff --git a/public/fonts/Ubuntu-Regular.ttf b/public/fonts/Ubuntu-Regular.ttf deleted file mode 100644 index f98a2da..0000000 Binary files a/public/fonts/Ubuntu-Regular.ttf and /dev/null differ diff --git a/src/App.jsx b/src/App.jsx index dafc3f5..24610b0 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,4 +1,4 @@ -import '@/css/App.css' +import '@/css/index.css' import 'bootstrap/dist/css/bootstrap.min.css' import 'bootstrap/dist/js/bootstrap.bundle.min.js' @@ -10,6 +10,8 @@ import Link from '@/components/Link.jsx' import NavBar from '@/components/NavBar.jsx' import Footer from '@/components/Footer.jsx' import { Routes, Route } from 'react-router-dom' +import HackerCat from '@/components/HackerCat' +import { useEffect } from 'react' const App = () => { @@ -23,6 +25,32 @@ const App = () => { return delta + (septemberOrLater ? 1 : 0); }; + useEffect(() => { + const setRandomFavicon = () => { + const start = 0x1F300; + const end = 0x1F64F; + + const randomCode = Math.floor(Math.random() * (end - start + 1)) + start; + + const hexCode = randomCode.toString(16); + + const twemojiUrl = `https://cdn.jsdelivr.net/gh/twitter/twemoji@latest/assets/svg/${hexCode}.svg`; + + let link = document.querySelector("link[rel~='icon']"); + if (!link) { + link = document.createElement('link'); + link.rel = 'icon'; + document.head.appendChild(link); + } + + link.href = twemojiUrl; + + link.type = 'image/x-icon'; + }; + + setRandomFavicon(); + }, []); + return ( <>
@@ -35,6 +63,8 @@ const App = () => { } />
diff --git a/src/components/Link.jsx b/src/components/Link.jsx index 14685c4..5a6b9db 100644 --- a/src/components/Link.jsx +++ b/src/components/Link.jsx @@ -1,5 +1,5 @@ import PropTypes from 'prop-types'; -import AbstractLink from './AbstractLink'; +import AbstractLink from '@/components/AbstractLink'; const Link = ({ to, children, isNavbar, className = '', ...props }) => { return ( diff --git a/src/components/NavBar.jsx b/src/components/NavBar.jsx index 4ad7058..683d45e 100644 --- a/src/components/NavBar.jsx +++ b/src/components/NavBar.jsx @@ -1,6 +1,6 @@ import { motion } from 'framer-motion'; import PropTypes from 'prop-types'; -import '../css/NavBar.css'; +import '@/css/NavBar.css'; const NavBar = ({ children }) => { const navVariants = { @@ -20,7 +20,9 @@ const NavBar = ({ children }) => { aria-expanded="false" aria-label="Toggle navigation" > - + + + - {theme === "dark" ? "☀️" : "🌙"} - - ); -} diff --git a/src/contexts/ConfigContext.jsx b/src/contexts/ConfigContext.jsx deleted file mode 100644 index 5012697..0000000 --- a/src/contexts/ConfigContext.jsx +++ /dev/null @@ -1,60 +0,0 @@ -import { createContext, useContext, useState, useEffect } from "react"; -import PropTypes from "prop-types"; - -/** - * ConfigContext.jsx - * - * Este archivo define el contexto de configuración para la aplicación, permitiendo cargar y manejar la configuración desde un archivo externo. - * - * Importaciones: - * - createContext, useContext, useState, useEffect: Funciones de React para crear y utilizar contextos, manejar estados y efectos secundarios. - * - PropTypes: Librería para la validación de tipos de propiedades en componentes de React. - * - * Funcionalidad: - * - ConfigContext: Contexto que almacena la configuración cargada, el estado de carga y cualquier error ocurrido durante la carga de la configuración. - * - ConfigProvider: Proveedor de contexto que maneja la carga de la configuración y proporciona el estado de la configuración a los componentes hijos. - * - Utiliza `fetch` para cargar la configuración desde un archivo JSON. - * - Maneja el estado de carga y errores durante la carga de la configuración. - * - useConfig: Hook personalizado para acceder al contexto de configuración. - * - * PropTypes: - * - ConfigProvider espera un único hijo (`children`) que es requerido y debe ser un nodo de React. - * - */ - -const ConfigContext = createContext(); - -export const ConfigProvider = ({ children }) => { - const [config, setConfig] = useState(null); - const [configLoading, setLoading] = useState(true); - const [configError, setError] = useState(null); - - useEffect(() => { - const fetchConfig = async () => { - try { - const response = await fetch("/config/settings.json"); - if (!response.ok) throw new Error("Error al cargar settings.json"); - const json = await response.json(); - setConfig(json); - } catch (err) { - setError(err.message); - } finally { - setLoading(false); - } - }; - - fetchConfig(); - }, []); - - return ( - - {children} - - ); -}; - -ConfigProvider.propTypes = { - children: PropTypes.node.isRequired, -}; - -export const useConfig = () => useContext(ConfigContext); diff --git a/src/contexts/DataContext.jsx b/src/contexts/DataContext.jsx deleted file mode 100644 index 079d641..0000000 --- a/src/contexts/DataContext.jsx +++ /dev/null @@ -1,67 +0,0 @@ -import { createContext, useContext, useState, useEffect } from "react"; -import PropTypes from "prop-types"; - -/** - * DataContext.jsx - * - * Este archivo define el contexto de datos para la aplicación, permitiendo obtener y manejar datos de una fuente externa. - * - * Importaciones: - * - createContext, useContext, useState, useEffect: Funciones de React para crear y utilizar contextos, manejar estados y efectos secundarios. - * - PropTypes: Librería para la validación de tipos de propiedades en componentes de React. - * - * Funcionalidad: - * - DataContext: Contexto que almacena los datos obtenidos, el estado de carga y cualquier error ocurrido durante la obtención de datos. - * - DataProvider: Proveedor de contexto que maneja la obtención de datos y proporciona el estado de los datos a los componentes hijos. - * - Utiliza `fetch` para obtener datos de una URL construida a partir de la configuración proporcionada. - * - Maneja el estado de carga y errores durante la obtención de datos. - * - useData: Hook personalizado para acceder al contexto de datos. - * - * PropTypes: - * - DataProvider espera un único hijo (`children`) que es requerido y debe ser un nodo de React. - * - DataProvider también espera una configuración (`config`) que debe incluir `baseUrl` (string) y opcionalmente `params` (objeto). - * - */ - -const DataContext = createContext(); - -export const DataProvider = ({ children, config }) => { - const [data, setData] = useState(null); - const [dataLoading, setLoading] = useState(true); - const [dataError, setError] = useState(null); - - useEffect(() => { - const fetchData = async () => { - try { - const queryParams = new URLSearchParams(config.params).toString(); - const url = `${config.baseUrl}?${queryParams}`; - const response = await fetch(url); - if (!response.ok) throw new Error("Error al obtener datos"); - const result = await response.json(); - setData(result); - } catch (err) { - setError(err.message); - } finally { - setLoading(false); - } - }; - - fetchData(); - }, [config]); - - return ( - - {children} - - ); -}; - -DataProvider.propTypes = { - children: PropTypes.node.isRequired, - config: PropTypes.shape({ - baseUrl: PropTypes.string.isRequired, - params: PropTypes.object, - }).isRequired, -}; - -export const useData = () => useContext(DataContext); \ No newline at end of file diff --git a/src/contexts/ThemeContext.jsx b/src/contexts/ThemeContext.jsx deleted file mode 100644 index 3337073..0000000 --- a/src/contexts/ThemeContext.jsx +++ /dev/null @@ -1,55 +0,0 @@ -import { createContext, useContext, useEffect, useState } from "react"; -import PropTypes from "prop-types"; - -/** - * ThemeContext.jsx - * - * Este archivo define el contexto de tema para la aplicación, permitiendo cambiar entre temas claro y oscuro. - * - * Importaciones: - * - createContext, useContext, useEffect, useState: Funciones de React para crear y utilizar contextos, manejar efectos secundarios y estados. - * - PropTypes: Librería para la validación de tipos de propiedades en componentes de React. - * - * Funcionalidad: - * - ThemeContext: Contexto que almacena el tema actual y la función para cambiarlo. - * - ThemeProvider: Proveedor de contexto que maneja el estado del tema y proporciona la función para alternar entre temas. - * - Utiliza `localStorage` para persistir el tema seleccionado. - * - Aplica la clase correspondiente al `body` del documento para reflejar el tema actual. - * - useTheme: Hook personalizado para acceder al contexto del tema. - * - * PropTypes: - * - ThemeProvider espera un único hijo (`children`) que es requerido y debe ser un nodo de React. - * - */ - -const ThemeContext = createContext(); - -export function ThemeProvider({ children }) { - const [theme, setTheme] = useState(() => { - return localStorage.getItem("theme") || "light"; - }); - - useEffect(() => { - document.body.classList.remove("light", "dark"); - document.body.classList.add(theme); - localStorage.setItem("theme", theme); - }, [theme]); - - const toggleTheme = () => { - setTheme(prevTheme => (prevTheme === "light" ? "dark" : "light")); - }; - - return ( - - {children} - - ); -} - -ThemeProvider.propTypes = { - children: PropTypes.node.isRequired, -}; - -export function useTheme() { - return useContext(ThemeContext); -} diff --git a/src/css/App.css b/src/css/App.css deleted file mode 100644 index 7f52bb7..0000000 --- a/src/css/App.css +++ /dev/null @@ -1,14 +0,0 @@ -body { - font-family: 'Poppins', sans-serif; - min-height: 100vh; -} - -body.light { - background: white; - color: black; -} - -body.dark { - background: linear-gradient(135deg, var(--gradient-primary), var(--gradient-secondary)); - color: white; -} \ No newline at end of file diff --git a/src/css/Card.css b/src/css/Card.css index 90c96cc..ae35fda 100644 --- a/src/css/Card.css +++ b/src/css/Card.css @@ -1,61 +1,50 @@ +.card-container { + perspective: 1000px; +} + .card { - border-radius: 20px; - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3); - transition: transform 0.3s ease, box-shadow 0.3s ease; - border: 2px solid var(--primary-color); -} - -.card.light { - background: linear-gradient(145deg, #eeeeee, #dadada); -} - -.card.light > div.card-content > p.card-text { - color: black; -} - -.card.light > span.status { - background: #E0E0E0; -} - -.card.dark { - background: linear-gradient(145deg, var(--card-gradient-primary), var(--card-gradient-secondary)); -} - -.card.dark > div.card-content > p.card-text { - color: white; -} - -.card.dark > span.status { - background: #505050; + border-radius: 16px; + transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); + border: 1px solid rgba(255, 63, 31, 0.3); + overflow: hidden; + display: flex; + flex-direction: column; + justify-content: space-between; + height: 100%; + background: linear-gradient(145deg, #1e1e1e, #252525); } .card:hover { - transform: translateY(-8px) scale(1.02); - box-shadow: 0 10px 20px var(--box-shadow); + transform: translateY(-10px) rotateX(2deg); + box-shadow: 0 15px 30px rgba(255, 63, 31, 0.3); + border-color: var(--primary-color); } -.card>h3 { - font-size: 1.3em; +.card h3 { + font-size: 1.1em; + font-family: 'Product Sans Bold', sans-serif; color: var(--primary-color); + margin-bottom: 15px; +} + +.card-content { + font-size: 0.95em; + font-family: 'Open Sans', sans-serif; + line-height: 1.5; + flex-grow: 1; + color: var(--text-color); +} + +.card-text { + color: var(--text-color) !important; +} + +.status { + font-family: 'Fira Code', monospace; + font-size: 0.75em; text-transform: uppercase; letter-spacing: 1px; - font-weight: 600; -} - -.card > h3 > .svg-inline--fa { - margin-right: 10px; -} - -p.card-text { - font-size: 2.2em; - font-weight: 600; -} - -.card>span.status { - font-size: 0.9em; - color: #A0A0A0; - padding: 5px 10px; - background: var(--card-background); - border-radius: 20px; - display: inline-block; + font-weight: bold; + border: 1px solid rgba(255, 63, 31, 0.4); + color: var(--text-color); } \ No newline at end of file diff --git a/src/css/Footer.css b/src/css/Footer.css index 7deda20..51b7af1 100644 --- a/src/css/Footer.css +++ b/src/css/Footer.css @@ -1,15 +1,57 @@ -footer > p > a { - color: var(--primary-color); +.main-footer { + background-color: #141414; + border-top: 1px solid #343a40; + padding: 2rem 0; + color: #adb5bd; } -footer > p > a:hover { - color: var(--secondary-color); +.footer-container { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; } -footer > div.d-flex > li.nav-item > a { - color: var(--primary-color); +@media (min-width: 768px) { + .footer-container { + flex-direction: row; + justify-content: center; + } } -footer > div.d-flex > li.nav-item > a:hover { - color: var(--secondary-color); +.footer-dev-text { + margin: 0; + font-weight: 500; +} + +.footer-divider { + color: #495057; + display: none; +} + +@media (min-width: 768px) { + .footer-divider { + display: inline; + } +} + +.footer-links { + display: flex; + gap: 1.5rem; + list-style: none; + padding: 0; + margin: 0; +} + +/* Estilo para todos los enlaces del footer */ +.footer-links a, +.footer-links .link-component { + color: var(--primary-color, #00d1b2); + text-decoration: none; + transition: color 0.3s ease; + font-size: 0.95rem; +} + +.footer-links a:hover { + color: var(--secondary-color, #fff); } \ No newline at end of file diff --git a/src/css/Header.css b/src/css/Header.css index c4c7035..6333596 100644 --- a/src/css/Header.css +++ b/src/css/Header.css @@ -1,20 +1,40 @@ -/* src/css/Header.css */ .header { - padding: 1rem 0; /* py-4 */ - background-color: #212529; /* bg-dark */ - color: #fff; /* text-white */ - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* shadow-lg */ + padding: 4rem 0; + background-color: #141414; + color: #fff; + text-align: center; + overflow: hidden; } .header-title { - font-size: 2.5rem; /* display-5 */ - font-weight: 700; /* fw-bold */ - margin-bottom: 1rem; /* mb-4 */ - color: var(--primary-color); /* Color azul de Bootstrap */ + font-family: 'Fira Code'; + font-size: clamp(1.6rem, 7vw, 3.5rem); + font-weight: 800; + margin-bottom: 0.5rem; + color: var(--primary-color); + white-space: nowrap; + min-height: 1.2em; + display: flex; + justify-content: center; + align-items: center; +} + +.header-title::after { + content: "_"; + margin-left: 5px; + color: var(--primary-color); + animation: blink 1s infinite; +} + +@keyframes blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } } .header-subtitle { - font-size: 1.25rem; /* lead */ - color: #6c757d; /* text-muted */ -} - + font-size: clamp(1rem, 3vw, 1.25rem); + font-family: 'Fira Code'; + color: #adb5bd; + max-width: 600px; + margin: 0 auto; +} \ No newline at end of file diff --git a/src/css/Home.css b/src/css/Home.css new file mode 100644 index 0000000..e90e09b --- /dev/null +++ b/src/css/Home.css @@ -0,0 +1,57 @@ +.home-container { + background: linear-gradient(135deg, var(--gradient-primary), var(--gradient-secondary)); + color: white; + min-height: 100vh; +} + +.glass-card { + background: rgba(255, 63, 31, 0.05); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 63, 31, 0.2); + border-radius: 15px; + transition: all 0.3s ease; +} + +.glass-card:hover { + border-color: var(--primary-color); + box-shadow: 0 8px 32px 0 rgba(255, 63, 31, 0.2); + transform: translateY(-5px); +} + +.skill-badge { + background: rgba(255, 63, 31, 0.1) !important; + border: 1px solid var(--primary-color) !important; + color: white !important; + font-family: "Fira Code", monospace; +} + +.section-title { + font-family: "Product Sans Bold", sans-serif; + letter-spacing: 2px; + color: var(--primary-color); + position: relative; + display: inline-block; + margin-bottom: 2rem; +} + +.section-title::after { + content: ''; + position: absolute; + bottom: -10px; + left: 0; + width: 50px; + height: 3px; + background: var(--primary-color); +} + +.project-link { + color: white; + text-decoration: none; +} + +.interest-item { + background: rgba(255, 255, 255, 0.03); + border-left: 3px solid var(--primary-color); + padding: 1rem; + font-family: "Product Sans", sans-serif; +} \ No newline at end of file diff --git a/src/css/NavBar.css b/src/css/NavBar.css index c49d90e..7be637b 100644 --- a/src/css/NavBar.css +++ b/src/css/NavBar.css @@ -9,5 +9,40 @@ } nav.navbar { - background-color: var(--gradient-primary); -} \ No newline at end of file + background-color: rgb(40, 40, 40); + border-top: 1px solid #343a40; + border-bottom: 1px solid #343a40; +} + +.navbar-toggler { + border: none !important; + padding: 0.5rem; + outline: none !important; + box-shadow: none !important; +} + +.navbar-toggler-icon { + background-image: none !important; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + position: relative; + width: 30px; + height: 24px; +} + +.navbar-toggler-icon::before, +.navbar-toggler-icon::after, +.navbar-toggler-icon-custom-line { + content: ""; + display: block; + width: 100%; + height: 3px; + background-color: var(--primary-color); + border-radius: 3px; + transition: all 0.3s ease; +} + +.navbar-toggler-icon::before { transform: translateY(-8px); } +.navbar-toggler-icon::after { transform: translateY(8px); } \ No newline at end of file diff --git a/src/css/Projects.css b/src/css/Projects.css new file mode 100644 index 0000000..eeb5820 --- /dev/null +++ b/src/css/Projects.css @@ -0,0 +1,5 @@ +.projects-container { + background: linear-gradient(135deg, var(--gradient-primary), var(--gradient-secondary)) !important; + color: white !important; + min-height: 100vh !important; +} \ No newline at end of file diff --git a/src/css/index.css b/src/css/index.css index 3d3454e..6fea0fd 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -4,66 +4,55 @@ --text-shadow: #771e0e; --box-shadow: #a81900; + --text-color: white; + --gradient-primary: #1A1A1A; --gradient-secondary: #2A2A2A; - - --card-background: #ff3f1f1a; - --card-gradient-primary: #252525; - --card-gradient-secondary: #353535; } -.badge { - background-color: var(--secondary-color) !important; - color: var(--primary-color) !important; - background-color: rgba(255, 63, 31, 0.1) !important; -} - -h2.h3 { - text-transform: uppercase; -} - -.mastodon-verify { +.primary { color: var(--primary-color); } - -/* latin-ext */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v22/pxiEyp8kv8JHgFVrJJnecmNE.woff2) format('woff2'); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +html, +body { + font-family: "Open Sans"; } -/* latin */ -@font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 400; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v22/pxiEyp8kv8JHgFVrJJfecg.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +.custom-hr { + height: 1px; + border: none; + background: linear-gradient(to right, transparent, var(--primary-color), transparent); + opacity: 0.4; + margin: 4rem 0; } -/* latin-ext */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v22/pxiByp8kv8JHgFVrLEj6Z1JlFc-K.woff2) format('woff2'); - unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; + font-family: "Fira Code"; + src: url('/fonts/FiraCode.ttf'); } -/* latin */ @font-face { - font-family: 'Poppins'; - font-style: normal; - font-weight: 600; - font-display: swap; - src: url(https://fonts.gstatic.com/s/poppins/v22/pxiByp8kv8JHgFVrLEj6Z1xlFQ.woff2) format('woff2'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + font-family: "Open Sans"; + src: url('/fonts/OpenSans.ttf'); } +@font-face { + font-family: "Product Sans"; + src: url('/fonts/ProductSansRegular.ttf'); +} + +@font-face { + font-family: "Product Sans Italic"; + src: url('/fonts/ProductSansItalic.ttf'); +} + +@font-face { + font-family: "Product Sans Italic Bold"; + src: url('/fonts/ProductSansBoldItalic.ttf'); +} + +@font-face { + font-family: "Product Sans Bold"; + src: url('/fonts/ProductSansBold.ttf'); +} diff --git a/src/pages/Home/Home.jsx b/src/pages/Home/Home.jsx index 0af75a5..e5486d1 100644 --- a/src/pages/Home/Home.jsx +++ b/src/pages/Home/Home.jsx @@ -1,11 +1,15 @@ import { motion } from 'framer-motion'; +import '@/css/Home.css'; +import Card from '@/components/Card'; const Home = () => { return ( -
-
+
+
+
+
@@ -14,111 +18,122 @@ const Home = () => { const AboutSection = () => { return ( -
-
-
- -
+
+
+ +
+
+ +
- - -

Sobre Mí

-

- Soy un estudiante de Ingeniería de Computadores en la Universidad de Sevilla, en mi cuarto año. - Actualmente me dedico a linux, redes, microcontroladores y webdev. Por aquí abajo dejo con lo que suelo trastear: -

-
- {['C', 'Rust', 'Java', 'JS (Vanilla + React)', 'Python', 'DB', 'Linux', 'HDL'].map((skill) => ( - - {skill} - - ))} -
-
-
-
-
- ); -}; +
+
-const InterestsSection = () => { - return ( -
-
-

Intereses

-
- {['Informática', 'Electrónica', 'Videojuegos', 'Música', 'Animanga', 'Ciberseguridad'].map((interest, index) => ( - -
- {interest} -
-
- ))} -
+ +

SOBRE MÍ

+

+ Estudiante de 4º de Ingeniería de Computadores en la US. + Suelo trastear con Linux, redes, electrónica y webdev. +

+
+ {['C', 'Rust', 'Java', 'JS', 'React', 'Python', 'Linux', 'HDL'].map((skill) => ( + + {skill} + + ))} +
+
); }; const ProjectsSection = () => { + const projects = [ + { title: 'miarma-backend', desc: 'Backend Spring que alimenta todos mis servicios', tech: 'Java/Spring' }, + { title: 'riscv-ac', desc: 'Implementación HDL del procesador RISC-V. Arquitectura pura.', tech: 'Verilog/VHDL' }, + { title: 'contaminus', desc: 'Proyecto Hack4Change 24/25. Fullstack + IoT.', tech: 'React/Python/IoT' }, + ]; + return ( -
-
-

Mis Proyectos Favoritos

-
- {[ - { title: 'miarma-backend', desc: 'Backend Spring que alimenta todos mis servicios' }, - { title: 'riscv-ac', desc: 'Implementación HDL del procesador RISC-V de la asignatura AC' }, - { title: 'contaminus', desc: 'Proyecto presentado al hackathon Hack4Change 24/25 de la ETSII (aunque el jurado no nos echase mucha cuenta...). Mezcla frontend, backend e IoT.' }, - ].map((project, index) => ( - +

MIS PROYECTOS FAVORITOS

+ +
+ ); +}; + +const InterestsSection = () => { + const interests = ['Informática', 'Electrónica', 'Videojuegos', 'Música', 'Animanga', 'Ciberseguridad']; + return ( +
+

INTERESES

+
+ {interests.map((interest, i) => ( + +
+ {interest} +
+
+ ))}
); diff --git a/src/pages/Projects/Projects.jsx b/src/pages/Projects/Projects.jsx index ba5c4a0..b736e09 100644 --- a/src/pages/Projects/Projects.jsx +++ b/src/pages/Projects/Projects.jsx @@ -1,49 +1,77 @@ import { useEffect, useState } from "react"; +import Card from "@/components/Card"; +import { motion } from "framer-motion"; +import '@/css/Projects.css' export default function ProjectsLanding() { const [repos, setRepos] = useState([]); + const [loading, setLoading] = useState(true); useEffect(() => { const fetchRepos = async () => { - try { - const userResponse = await fetch("https://api.github.com/users/Gallardo7761/repos"); - const userRepos = await userResponse.json(); + const GITEA_URL = "https://git.miarma.net"; + const USERNAME = "Gallardo7761"; - setRepos(userRepos); + try { + const response = await fetch(`${GITEA_URL}/api/v1/users/${USERNAME}/repos?limit=100`); + if (!response.ok) throw new Error("Fallo en la conexión"); + const data = await response.json(); + setRepos(data); } catch (error) { - console.error("Error al obtener repos:", error); + console.error("Error:", error); + } finally { + setLoading(false); } }; fetchRepos(); }, []); return ( -
-

Mis Proyectos en GitHub

-

- (Copia temporal del mirror de GitHub, próximamente desde mi propia instancia Gitea!!!) -

-
); -} +} \ No newline at end of file