├── src
├── vite-env.d.ts
├── components
│ ├── accordion-2
│ │ ├── 1.jpg
│ │ ├── 2.jpg
│ │ ├── 3.jpg
│ │ ├── 4.jpg
│ │ ├── 5.jpg
│ │ ├── styles.css
│ │ └── Accordion.tsx
│ └── accordion-1
│ │ ├── image.jpeg
│ │ ├── styles.css
│ │ └── Accordion.tsx
├── App.tsx
├── index.css
├── main.tsx
├── App.css
└── assets
│ └── react.svg
├── vite.config.ts
├── tsconfig.node.json
├── .gitignore
├── .eslintrc.cjs
├── index.html
├── tsconfig.json
├── package.json
└── public
└── vite.svg
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/src/components/accordion-2/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontend-joe/react-accordions/HEAD/src/components/accordion-2/1.jpg
--------------------------------------------------------------------------------
/src/components/accordion-2/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontend-joe/react-accordions/HEAD/src/components/accordion-2/2.jpg
--------------------------------------------------------------------------------
/src/components/accordion-2/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontend-joe/react-accordions/HEAD/src/components/accordion-2/3.jpg
--------------------------------------------------------------------------------
/src/components/accordion-2/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontend-joe/react-accordions/HEAD/src/components/accordion-2/4.jpg
--------------------------------------------------------------------------------
/src/components/accordion-2/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontend-joe/react-accordions/HEAD/src/components/accordion-2/5.jpg
--------------------------------------------------------------------------------
/src/components/accordion-1/image.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/frontend-joe/react-accordions/HEAD/src/components/accordion-1/image.jpeg
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import { Accordion } from "./components/accordion-2/Accordion";
2 |
3 | function App() {
4 | return ;
5 | }
6 |
7 | export default App;
8 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | display: flex;
4 | justify-content: center;
5 | align-items: center;
6 | height: 100vh;
7 | color: #3e3f5d;
8 | font-family: "Euclid Circular A", "Poppins";
9 | }
10 |
--------------------------------------------------------------------------------
/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "bundler",
7 | "allowSyntheticDefaultImports": true
8 | },
9 | "include": ["vite.config.ts"]
10 | }
11 |
--------------------------------------------------------------------------------
/src/main.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import ReactDOM from 'react-dom/client'
3 | import App from './App.tsx'
4 | import './index.css'
5 |
6 | ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
7 |
8 |
9 | ,
10 | )
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: { browser: true, es2020: true },
3 | extends: [
4 | 'eslint:recommended',
5 | 'plugin:@typescript-eslint/recommended',
6 | 'plugin:react-hooks/recommended',
7 | ],
8 | parser: '@typescript-eslint/parser',
9 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
10 | plugins: ['react-refresh'],
11 | rules: {
12 | 'react-refresh/only-export-components': 'warn',
13 | },
14 | }
15 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React + TS
8 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "lib": ["DOM", "DOM.Iterable", "ESNext"],
5 | "module": "ESNext",
6 | "skipLibCheck": true,
7 |
8 | /* Bundler mode */
9 | "moduleResolution": "bundler",
10 | "allowImportingTsExtensions": true,
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "noEmit": true,
14 | "jsx": "react-jsx",
15 |
16 | /* Linting */
17 | "strict": true,
18 | "noUnusedLocals": true,
19 | "noUnusedParameters": true,
20 | "noFallthroughCasesInSwitch": true
21 | },
22 | "include": ["src"],
23 | "references": [{ "path": "./tsconfig.node.json" }]
24 | }
25 |
--------------------------------------------------------------------------------
/src/App.css:
--------------------------------------------------------------------------------
1 | #root {
2 | max-width: 1280px;
3 | margin: 0 auto;
4 | padding: 2rem;
5 | text-align: center;
6 | }
7 |
8 | .logo {
9 | height: 6em;
10 | padding: 1.5em;
11 | will-change: filter;
12 | transition: filter 300ms;
13 | }
14 | .logo:hover {
15 | filter: drop-shadow(0 0 2em #646cffaa);
16 | }
17 | .logo.react:hover {
18 | filter: drop-shadow(0 0 2em #61dafbaa);
19 | }
20 |
21 | @keyframes logo-spin {
22 | from {
23 | transform: rotate(0deg);
24 | }
25 | to {
26 | transform: rotate(360deg);
27 | }
28 | }
29 |
30 | @media (prefers-reduced-motion: no-preference) {
31 | a:nth-of-type(2) .logo {
32 | animation: logo-spin infinite 20s linear;
33 | }
34 | }
35 |
36 | .card {
37 | padding: 2em;
38 | }
39 |
40 | .read-the-docs {
41 | color: #888;
42 | }
43 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-accordions",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "tsc && vite build",
9 | "lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
10 | "preview": "vite preview"
11 | },
12 | "dependencies": {
13 | "react": "^18.2.0",
14 | "react-dom": "^18.2.0"
15 | },
16 | "devDependencies": {
17 | "@types/react": "^18.0.28",
18 | "@types/react-dom": "^18.0.11",
19 | "@typescript-eslint/eslint-plugin": "^5.57.1",
20 | "@typescript-eslint/parser": "^5.57.1",
21 | "@vitejs/plugin-react": "^4.0.0",
22 | "eslint": "^8.38.0",
23 | "eslint-plugin-react-hooks": "^4.6.0",
24 | "eslint-plugin-react-refresh": "^0.3.4",
25 | "typescript": "^5.0.2",
26 | "vite": "^4.3.2"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/accordion-1/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | display: grid;
4 | place-items: center;
5 | height: 100vh;
6 | color: #3e3f5d;
7 | background: #eff0ff;
8 | font-family: "Euclid Circular A", "Poppins";
9 | }
10 |
11 | article {
12 | background: #ffffff;
13 | width: 400px;
14 | padding: 20px;
15 | border-radius: 10px;
16 | }
17 |
18 | article header {
19 | display: flex;
20 | align-items: center;
21 | justify-content: space-between;
22 | height: 48px;
23 | padding: 0 10px 0 20px;
24 | border-radius: 6px;
25 | cursor: pointer;
26 | background: #494964;
27 | margin-bottom: 10px;
28 | color: #f8f8f8;
29 | }
30 |
31 | article header.active {
32 | background: #6366f1;
33 | }
34 |
35 | article header.active span {
36 | rotate: -180deg;
37 | }
38 |
39 | article h2 {
40 | margin: 0;
41 | font-size: 16px;
42 | font-weight: 500;
43 | }
44 |
45 | article p {
46 | padding: 0 20px 10px;
47 | line-height: 1.7;
48 | font-size: 14px;
49 | }
50 |
51 | article span {
52 | transition: 0.3s;
53 | }
54 |
55 | .collapse {
56 | position: relative;
57 | height: 0;
58 | overflow: hidden;
59 | transition: height 0.5s ease;
60 | }
61 |
62 | .collapse.show {
63 | height: auto;
64 | }
65 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/accordion-2/styles.css:
--------------------------------------------------------------------------------
1 | section {
2 | display: flex;
3 | gap: 10px;
4 | cursor: pointer;
5 | }
6 |
7 | article {
8 | position: relative;
9 | overflow: hidden;
10 | background: #ffffff;
11 | width: 64px;
12 | height: 500px;
13 | border-radius: 36px;
14 | display: flex;
15 | align-items: flex-end;
16 | opacity: 0.9;
17 | transition: 0.5s;
18 | }
19 |
20 | h2,
21 | p {
22 | margin: 0;
23 | }
24 |
25 | article h2 {
26 | font-size: 24px;
27 | font-weight: 400;
28 | color: rgb(255 255 255 / 96%);
29 | }
30 |
31 | article p {
32 | color: rgb(255 255 255 / 66%);
33 | }
34 |
35 | article.active {
36 | width: 400px;
37 | opacity: 1;
38 | }
39 |
40 | article .material-symbols-outlined {
41 | display: grid;
42 | place-items: center;
43 | width: 50px;
44 | height: 50px;
45 | background: rgb(255 255 255 / 80%);
46 | border-radius: 50%;
47 | font-size: 28px;
48 | }
49 |
50 | article .content {
51 | position: absolute;
52 | bottom: 0;
53 | left: 0;
54 | width: 400px;
55 | z-index: 1;
56 | opacity: 0;
57 | visibility: hidden;
58 | padding: 100px 0 20px 20px;
59 | display: flex;
60 | align-items: center;
61 | gap: 14px;
62 | background: linear-gradient(to bottom, rgb(0 0 0 / 0%), rgb(0 0 0 / 80%));
63 | transition: 0.25s;
64 | }
65 |
66 | article.active .content {
67 | opacity: 1;
68 | visibility: visible;
69 | }
70 |
71 | article img {
72 | position: absolute;
73 | z-index: 0;
74 | top: 50%;
75 | left: 50%;
76 | translate: -50% -50%;
77 | height: 150%;
78 | filter: grayscale(0.5);
79 | }
80 |
--------------------------------------------------------------------------------
/src/components/accordion-2/Accordion.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import image1 from "./1.jpg";
3 | import image2 from "./2.jpg";
4 | import image3 from "./3.jpg";
5 | import image4 from "./4.jpg";
6 | import image5 from "./5.jpg";
7 | import "./styles.css";
8 |
9 | const cards = [
10 | {
11 | header: "Canada",
12 | image: image2,
13 | text: `Image description here`,
14 | },
15 | {
16 | header: "Bali",
17 | image: image1,
18 | text: `Image description here`,
19 | },
20 | {
21 | header: "Spain",
22 | image: image3,
23 | text: `Image description here`,
24 | },
25 | {
26 | header: "Indonesia",
27 | image: image4,
28 | text: `Image description here`,
29 | },
30 | {
31 | header: "South Africa",
32 | image: image5,
33 | text: `Image description here`,
34 | },
35 | ];
36 |
37 | export const Accordion = () => {
38 | const [active, setActive] = useState(0);
39 |
40 | const handleToggle = (index: number) => setActive(index);
41 |
42 | return (
43 |
44 | {cards.map((card, index) => {
45 | const isActive = active === index ? "active" : "";
46 | return (
47 | handleToggle(index)}
51 | >
52 |
53 |
54 |
photo_camera
55 |
56 |
{card.header}
57 |
{card.text}
58 |
59 |
60 |
61 | );
62 | })}
63 |
64 | );
65 | };
66 |
--------------------------------------------------------------------------------
/src/components/accordion-1/Accordion.tsx:
--------------------------------------------------------------------------------
1 | import { useRef, useState } from "react";
2 | import "./styles.css";
3 |
4 | const faqs = [
5 | {
6 | id: 1,
7 | header: "What is Lorem Ipsum?",
8 | text: `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.`,
9 | },
10 | {
11 | id: 2,
12 | header: "Where does it come from?",
13 | text: `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.`,
14 | },
15 | {
16 | id: 3,
17 | header: "Why do we use it?",
18 | text: `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.`,
19 | },
20 | {
21 | id: 4,
22 | header: "Where can I get some?",
23 | text: `Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.`,
24 | },
25 | ];
26 |
27 | const AccordionItem = (props: any) => {
28 | const contentEl = useRef(null);
29 | const { handleToggle, active, faq } = props;
30 | const { header, id, text } = faq;
31 |
32 | return (
33 |
34 |
handleToggle(id)}
37 | >
38 | {header}
39 | expand_more
40 |
41 |
52 |
53 | );
54 | };
55 |
56 | export const Accordion = () => {
57 | const [active, setActive] = useState(null);
58 |
59 | const handleToggle = (index: any) => {
60 | if (active === index) {
61 | setActive(null);
62 | } else {
63 | setActive(index);
64 | }
65 | };
66 |
67 | return (
68 |
69 | {faqs.map((faq, index) => {
70 | return (
71 |
77 | );
78 | })}
79 |
80 | );
81 | };
82 |
--------------------------------------------------------------------------------
/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------