Merge pull request #124 from BreizhHardware/refactor/new-skill-system

Enhance skill display and add tooltip for skill levels
This commit is contained in:
Félix MARQUET
2025-03-06 10:27:45 +01:00
committed by GitHub
9 changed files with 387 additions and 151 deletions

23
.gitignore vendored
View File

@@ -26,3 +26,26 @@ yarn-error.log*
.idea
dist
/src/components/About.js
/src/components/Card.js
/src/components/CV.js
/src/assets/DATA.js
/src/components/Footer.js
/cypress/fixtures/test-redirections/idObjES6Export.js
/cypress/fixtures/test-redirections/idObjES6Import.js
/cypress/fixtures/test-redirections/idObjES6ImportExport.js
/cypress/fixtures/__tests__/import-es6-export-test.js
/cypress/fixtures/__tests__/import-es6-import-export-test.js
/cypress/fixtures/__tests__/import-es6-import-test.js
/cypress/fixtures/__tests__/import-vanilla-test.js
/cypress/fixtures/__tests__/index-test.js
/src/components/Menu.js
/src/components/ProgresBar.js
/src/components/Project.js
/src/components/ProjectCard.js
/cypress/fixtures/__tests__/require-es6-export-test.js
/cypress/fixtures/__tests__/require-es6-import-export-test.js
/cypress/fixtures/__tests__/require-es6-import-test.js
/cypress/fixtures/__tests__/require-vanilla-test.js
/src/components/SkillCard.js
/src/components/Skills.js

9
package-lock.json generated
View File

@@ -2823,9 +2823,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001692",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz",
"integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==",
"version": "1.0.30001702",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001702.tgz",
"integrity": "sha512-LoPe/D7zioC0REI5W73PeR1e1MLCipRGq/VkovJnd6Df+QVqT+vT33OXCp8QUd7kA7RZrHWxb1B36OQKI/0gOA==",
"funding": [
{
"type": "opencollective",
@@ -2839,7 +2839,8 @@
"type": "github",
"url": "https://github.com/sponsors/ai"
}
]
],
"license": "CC-BY-4.0"
},
"node_modules/caseless": {
"version": "0.12.0",

View File

@@ -25,7 +25,7 @@
"start": "vite",
"build": "npx tsc && vite build",
"serve": "vite preview",
"build:css": "tailwindcss build src/index.css -o src/output.css",
"build:css": "tailwindcss build -i src/index.css -o src/output.css",
"cypress:open": "cypress open",
"cypress:run": "cypress run",
"test": "browserslist && cypress run"
@@ -52,7 +52,6 @@
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@simonsmith/cypress-image-snapshot": "^9.1.0",
"@vitejs/plugin-react": "^4.3.4",
"tailwindcss": "^3.4.17",
"vite": "^6.2.0",
"vite-plugin-svgr": "^4.3.0",
"vite-tsconfig-paths": "^5.1.4"

View File

@@ -14,11 +14,11 @@ const Felix = {
skills: [
{
skillName: "C",
skillLevel: 80,
skillLevel: 85,
},
{
skillName: "C++",
skillLevel: 75,
skillLevel: 80,
},
{
skillName: "Admin Système",
@@ -26,7 +26,7 @@ const Felix = {
},
{
skillName: "Python",
skillLevel: 75,
skillLevel: 80,
},
{
skillName: "PHP",
@@ -42,7 +42,7 @@ const Felix = {
},
{
skillName: "Linux",
skillLevel: 75,
skillLevel: 80,
},
{
skillName: "Go",
@@ -58,7 +58,7 @@ const Felix = {
},
{
skillName: "React",
skillLevel: 70,
skillLevel: 80,
}
],
projects: [

View File

@@ -12,9 +12,9 @@ import {
FaRust,
FaServer
} from 'react-icons/fa';
//@ts-ignore
import ProgresBar from "./ProgresBar.tsx";
import {FaGolang} from "react-icons/fa6";
// @ts-ignore
import SkillLevel from "./SkillLevel.tsx";
const iconClassName = "mx-auto text-4xl text-gray-800 dark:text-gray-200";
@@ -45,10 +45,10 @@ type IconMapping = {
const SkillCard: React.FC<SkillCardProps> = ({skillName, skillLevel}) => {
const skillIcon = iconMapping[skillName];
return(
<div className="m-4 w-40 flex-none mx-auto text-center p-5 rounded-xl border-2 border-gray-300 ">
<div className="m-4 w-40 flex-none mx-auto text-center p-5 rounded-xl border-2 border-gray-300 dark:border-gray-700 hover:shadow-lg transition-shadow">
{skillIcon}
<p className="text-xl font-semibold mt-4 dark:text-gray-400">{skillName}</p>
<ProgresBar className="" bgcolor={"#1d2a44"} completed={skillLevel} ></ProgresBar>
<p className="text-xl font-semibold mt-4 text-gray-800 dark:text-gray-300">{skillName}</p>
<SkillLevel level={skillLevel} skillName={skillName} />
</div>
);
}

View File

@@ -0,0 +1,93 @@
import React, { useState, useRef, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
interface SkillLevelProps {
level: number;
skillName: string;
}
const SkillLevel: React.FC<SkillLevelProps> = ({ level, skillName }) => {
const [showTooltip, setShowTooltip] = useState(false);
const [tooltipPosition, setTooltipPosition] = useState({ top: 0, left: 0 });
const badgeRef = useRef<HTMLDivElement>(null);
const tooltipRef = useRef<HTMLDivElement>(null);
const { t } = useTranslation();
// Determine skill level text based on numeric value
let skillLevelText;
if (level < 60) {
skillLevelText = t('skills.beginner');
} else if (level < 80) {
skillLevelText = t('skills.intermediate');
} else {
skillLevelText = t('skills.expert');
}
const calculatePosition = () => {
if (badgeRef.current && tooltipRef.current) {
const badgeRect = badgeRef.current.getBoundingClientRect();
const tooltipRect = tooltipRef.current.getBoundingClientRect();
// Center tooltip horizontally
const left = badgeRect.left + (badgeRect.width / 2) - (tooltipRect.width / 2);
// Position tooltip directly below the badge (reduce gap)
const top = badgeRect.bottom + 5;
// Ensure tooltip stays within viewport
const adjustedLeft = Math.max(10, Math.min(left, window.innerWidth - tooltipRect.width - 10));
setTooltipPosition({ top, left: adjustedLeft });
}
};
useEffect(() => {
if (showTooltip) {
calculatePosition();
// Recalculate position on window resize
window.addEventListener('resize', calculatePosition);
window.addEventListener('scroll', calculatePosition);
return () => {
window.removeEventListener('resize', calculatePosition);
window.removeEventListener('scroll', calculatePosition);
};
}
}, [showTooltip]);
const handleTouch = (e: React.TouchEvent) => {
e.preventDefault();
setShowTooltip(!showTooltip);
};
return (
<div className="mt-2">
<div
ref={badgeRef}
className="inline-block px-3 py-1 rounded-full text-sm font-medium bg-gray-200 text-gray-800 border cursor-pointer dark:bg-gray-800 dark:text-gray-200 dark:border-gray-700"
onMouseEnter={() => setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)}
onTouchStart={handleTouch}
>
{skillLevelText}
</div>
{showTooltip && (
<div
ref={tooltipRef}
className="fixed z-50 w-64 p-3 text-sm bg-gray-200 rounded-xl border-2 border-gray-300 dark:border-gray-700 hover:shadow-lg dark:bg-gray-800 dark:text-gray-200 transition-shadow"
style={{
top: `${tooltipPosition.top}px`,
left: `${tooltipPosition.left}px`,
}}
>
<p className="font-bold mb-1">{t(`skills.examples.${skillName}.title`)}</p>
<p>{t(`skills.examples.${skillName}.description`)}</p>
</div>
)}
</div>
);
};
export default SkillLevel;

View File

@@ -8,7 +8,7 @@ i18n
fr: {
translation: {
"about.title": "A propos de moi",
"about.description": "Je suis étudiant en 3e année à l'ISEN Nantes en alternance chez Horoquartz. Je suis passionné par l'informatique. J'ai appris à coder en autodidacte et je suis actuellement en train d'apprendre le C++ et le PHP. Je suis également passionné par l'électronique et le hardware. Je possède un homelab composé de 2 serveur, un DELL T320 et un DELL T330 les 2 sous proxmox.",
"about.description": "Je suis étudiant en 3e année à l'ISEN Nantes en alternance chez Horoquartz. Je suis passionné par l'informatique. J'ai appris à coder en autodidacte et je suis actuellement en train d'apprendre le Rust et le Go. Je suis également passionné par l'électronique et le hardware. Je possède un homelab composé de 3serveur, un DELL T320, un DELL T330 et un Dell Precision T3610 les 3 sous proxmox.",
"card.title": "Etudiant en 3e année a l'ISEN Nantes en alternance chez Horoquartz",
"projects.title": "Mes projets",
"projects.Front end starter.description": "Mon starter personnel pour projet front end",
@@ -23,13 +23,40 @@ i18n
"nav.about": "A propos de moi",
"nav.projects": "Mes projets",
"nav.cv": "Mon CV",
"skills.beginner": "Débutant",
"skills.intermediate": "Intermédiaire",
"skills.expert": "Expert",
"skills.examples.C.title": "Projets C",
"skills.examples.C.description": "Projet de fin de 1ère année à l'ISEN, algorithmes de base, création d'une api REST",
"skills.examples.C++.title": "Projets C++",
"skills.examples.C++.description": "Tower Defense en Qt6, simulation de banc de poissons avec SDL2 avec support du multijoueur, algorithmes de base",
"skills.examples.Admin Système.title": "Administration Système",
"skills.examples.Admin Système.description": "Configuration de 3 serveurs DELL sous Proxmox, virtualisation, maintenance des machines virtuelles",
"skills.examples.Python.title": "Projets Python",
"skills.examples.Python.description": "Github NTFY pour les notifications des releases github et dockerhub, projets de cours",
"skills.examples.PHP.title": "Projets PHP",
"skills.examples.PHP.description": "Développements web avec PHP, AJAX, postgreSQL",
"skills.examples.HTML/CSS.title": "Développement Front-end",
"skills.examples.HTML/CSS.description": "Front-end starter personnalisé, projet de base, divers projets web",
"skills.examples.JS/TS.title": "JavaScript/TypeScript",
"skills.examples.JS/TS.description": "Développement React, ce portfolio, api NodeJS avec Express, Création d'une application mobile ISEN Orbit avec React Native, projets professionnels",
"skills.examples.Linux.title": "Administration Linux",
"skills.examples.Linux.description": "Configuration de serveurs, administration système, Ansible playbooks",
"skills.examples.Go.title": "Projets Go",
"skills.examples.Go.description": "Initiation au langage, Création d'une api REST avec Fiber, Go Gin et la librairie standard, projets professionnels",
"skills.examples.Docker.title": "Containerisation",
"skills.examples.Docker.description": "Déploiement de conteneurs, configuration Docker Compose, création d'images, déploiement de services en haute disponibilité via Swarm et Kubernetes",
"skills.examples.Rust.title": "Projets Rust",
"skills.examples.Rust.description": "Apprentissage du langage, création d'une api REST avec Ntex",
"skills.examples.React.title": "Développement React",
"skills.examples.React.description": "Ce portfolio, site web de Modelec, application mobile ISEN Orbit, projets professionnels",
},
},
en: {
translation: {
"cv.title": "My CV",
"about.title": "About me",
"about.description": "I am a thrid year student at ISEN Nantes in work-study program at Horoquartz. I am passionate about computer science. I learned to code on my own and I am currently learning C++ and PHP. I am also passionate about electronics and hardware. I have a homelab composed of 2 servers, a DELL T320 and a DELL T330 both under proxmox.",
"about.description": "I am a third-year student at ISEN Nantes, currently in a work-study program at Horoquartz. I am passionate about computer science. I learned to code on my own and am currently learning Rust and Go. I am also passionate about electronics and hardware. I have a homelab consisting of 3 servers: a DELL T320, a DELL T330, and a Dell Precision T3610, all running Proxmox.",
"card.title": "Third year student at ISEN Nantes in work-study program at Horoquartz",
"projects.title": "My projects",
"projects.Front end starter.description": "My personal starter for front end projects",
@@ -42,7 +69,34 @@ i18n
"nav.about": "About me",
"nav.projects": "My projects",
"nav.cv": "My CV",
"cv.path": "/CV%20Félix%20MARQUET%20ENGLISH.pdf"
"cv.path": "/CV%20Félix%20MARQUET%20ENGLISH.pdf",
"skills.beginner": "Beginner",
"skills.intermediate": "Intermediate",
"skills.expert": "Expert",
"skills.examples.C.title": "C Projects",
"skills.examples.C.description": "End-of-first-year project at ISEN, basic algorithms, creation of a REST API.",
"skills.examples.C++.title": "C++ Projects",
"skills.examples.C++.description": "Tower Defense in Qt6, fish school simulation with SDL2 featuring multiplayer support, basic algorithms.",
"skills.examples.Admin Système.title": "System Administration",
"skills.examples.Admin Système.description": "Configuration of 3 DELL servers under Proxmox, virtualization, maintenance of virtual machines.",
"skills.examples.Python.title": "Python Projects",
"skills.examples.Python.description": "GitHub NTFY for GitHub and DockerHub release notifications, course projects.",
"skills.examples.PHP.title": "PHP Projects",
"skills.examples.PHP.description": "Web development with PHP, AJAX, PostgreSQL.",
"skills.examples.HTML/CSS.title": "Front-end development",
"skills.examples.HTML/CSS.description": "Custom front-end starter, base project, various web projects.",
"skills.examples.JS/TS.title": "JavaScript/TypeScript",
"skills.examples.JS/TS.description": "React development, this portfolio, NodeJS API with Express, creation of the ISEN Orbit mobile application with React Native, professional projects.",
"skills.examples.Linux.title": "Linux Administration",
"skills.examples.Linux.description": "Server configuration, system administration, Ansible playbooks.",
"skills.examples.Go.title": "Go Projects",
"skills.examples.Go.description": "Introduction to the language, creation of a REST API with Fiber, Go Gin, and the standard library, professional projects.",
"skills.examples.Docker.title": "Containerization",
"skills.examples.Docker.description": "Container deployment, Docker Compose configuration, image creation, high-availability service deployment via Swarm and Kubernetes.",
"skills.examples.Rust.title": "Rust Projects",
"skills.examples.Rust.description": "Learning the language, creation of a REST API with Ntex.",
"skills.examples.React.title": "React Development",
"skills.examples.React.description": "This portfolio, Modelec website, ISEN Orbit mobile application, professional projects."
},
},
},

View File

@@ -1,17 +0,0 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

View File

@@ -1,5 +1,113 @@
*, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
}
/*
! tailwindcss v3.4.4 | MIT License | https://tailwindcss.com
! tailwindcss v3.4.14 | MIT License | https://tailwindcss.com
*/
/*
@@ -433,116 +541,42 @@ video {
/* Make elements with the HTML hidden attribute stay hidden by default */
[hidden] {
[hidden]:where(:not([hidden="until-found"])) {
display: none;
}
*, ::before, ::after {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
.container {
width: 100%;
}
::backdrop {
--tw-border-spacing-x: 0;
--tw-border-spacing-y: 0;
--tw-translate-x: 0;
--tw-translate-y: 0;
--tw-rotate: 0;
--tw-skew-x: 0;
--tw-skew-y: 0;
--tw-scale-x: 1;
--tw-scale-y: 1;
--tw-pan-x: ;
--tw-pan-y: ;
--tw-pinch-zoom: ;
--tw-scroll-snap-strictness: proximity;
--tw-gradient-from-position: ;
--tw-gradient-via-position: ;
--tw-gradient-to-position: ;
--tw-ordinal: ;
--tw-slashed-zero: ;
--tw-numeric-figure: ;
--tw-numeric-spacing: ;
--tw-numeric-fraction: ;
--tw-ring-inset: ;
--tw-ring-offset-width: 0px;
--tw-ring-offset-color: #fff;
--tw-ring-color: rgb(59 130 246 / 0.5);
--tw-ring-offset-shadow: 0 0 #0000;
--tw-ring-shadow: 0 0 #0000;
--tw-shadow: 0 0 #0000;
--tw-shadow-colored: 0 0 #0000;
--tw-blur: ;
--tw-brightness: ;
--tw-contrast: ;
--tw-grayscale: ;
--tw-hue-rotate: ;
--tw-invert: ;
--tw-saturate: ;
--tw-sepia: ;
--tw-drop-shadow: ;
--tw-backdrop-blur: ;
--tw-backdrop-brightness: ;
--tw-backdrop-contrast: ;
--tw-backdrop-grayscale: ;
--tw-backdrop-hue-rotate: ;
--tw-backdrop-invert: ;
--tw-backdrop-opacity: ;
--tw-backdrop-saturate: ;
--tw-backdrop-sepia: ;
--tw-contain-size: ;
--tw-contain-layout: ;
--tw-contain-paint: ;
--tw-contain-style: ;
@media (min-width: 640px) {
.container {
max-width: 640px;
}
}
@media (min-width: 768px) {
.container {
max-width: 768px;
}
}
@media (min-width: 1024px) {
.container {
max-width: 1024px;
}
}
@media (min-width: 1280px) {
.container {
max-width: 1280px;
}
}
@media (min-width: 1536px) {
.container {
max-width: 1536px;
}
}
.sr-only {
@@ -581,6 +615,10 @@ video {
z-index: 50;
}
.m-0 {
margin: 0px;
}
.m-1 {
margin: 0.25rem;
}
@@ -599,6 +637,10 @@ video {
margin-bottom: 1rem;
}
.mb-1 {
margin-bottom: 0.25rem;
}
.mb-4 {
margin-bottom: 1rem;
}
@@ -611,6 +653,10 @@ video {
margin-top: 4rem;
}
.mt-2 {
margin-top: 0.5rem;
}
.mt-3 {
margin-top: 0.75rem;
}
@@ -655,6 +701,10 @@ video {
width: 10rem;
}
.w-64 {
width: 16rem;
}
.w-auto {
width: auto;
}
@@ -675,6 +725,14 @@ video {
flex: none;
}
.cursor-pointer {
cursor: pointer;
}
.resize {
resize: both;
}
.flex-row {
flex-direction: row;
}
@@ -723,6 +781,10 @@ video {
border-radius: 0.75rem;
}
.border {
border-width: 1px;
}
.border-2 {
border-width: 2px;
}
@@ -745,6 +807,11 @@ video {
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
}
.bg-gray-200 {
--tw-bg-opacity: 1;
background-color: rgb(229 231 235 / var(--tw-bg-opacity));
}
.bg-white {
--tw-bg-opacity: 1;
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
@@ -758,6 +825,10 @@ video {
padding: 0.5rem;
}
.p-3 {
padding: 0.75rem;
}
.p-4 {
padding: 1rem;
}
@@ -830,6 +901,11 @@ video {
line-height: 1.5rem;
}
.text-sm {
font-size: 0.875rem;
line-height: 1.25rem;
}
.text-xl {
font-size: 1.25rem;
line-height: 1.75rem;
@@ -848,6 +924,10 @@ video {
font-weight: 800;
}
.font-medium {
font-weight: 500;
}
.font-semibold {
font-weight: 600;
}
@@ -912,6 +992,12 @@ video {
transition-duration: 150ms;
}
.transition-shadow {
transition-property: box-shadow;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.duration-300 {
transition-duration: 300ms;
}
@@ -930,6 +1016,12 @@ video {
color: rgb(255 255 255 / var(--tw-text-opacity));
}
.hover\:shadow-lg:hover {
--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.dark\:border-gray-700:is(.dark *) {
--tw-border-opacity: 1;
border-color: rgb(55 65 81 / var(--tw-border-opacity));
@@ -993,19 +1085,10 @@ video {
width: 41.666667%;
}
.sm\:flex-row {
flex-direction: row;
}
.sm\:p-2 {
padding: 0.5rem;
}
.sm\:px-5 {
padding-left: 1.25rem;
padding-right: 1.25rem;
}
.sm\:text-2xl {
font-size: 1.5rem;
line-height: 2rem;