1
1
mirror of https://github.com/theoludwig/theoludwig.git synced 2025-05-29 22:37:44 +02:00

chore: usage of prettier, eslint and lint-staged instead of ts-standard

This commit is contained in:
divlo
2021-04-27 19:09:33 +02:00
parent 2a24af7c47
commit 0901b07611
34 changed files with 1574 additions and 1152 deletions

6
.eslintignore Normal file
View File

@ -0,0 +1,6 @@
.next
.lighthouseci
node_modules
next-env.d.ts
**/workbox-*.js
**/sw.js

15
.eslintrc.json Normal file
View File

@ -0,0 +1,15 @@
{
"extends": ["standard-with-typescript", "eslint-config-prettier"],
"plugins": ["eslint-plugin-prettier"],
"parserOptions": {
"project": "./tsconfig.json"
},
"env": {
"node": true,
"browser": true,
"jest": true
},
"rules": {
"prettier/prettier": "error"
}
}

View File

@ -1,7 +1,4 @@
#!/bin/sh #!/bin/sh
. "$(dirname "$0")/_/husky.sh" . "$(dirname "$0")/_/husky.sh"
npm run lint:docker npm run lint:staged
npm run lint:editorconfig
npm run lint:markdown
npm run lint:typescript

7
.lintstagedrc.json Normal file
View File

@ -0,0 +1,7 @@
{
"*": ["editorconfig-checker"],
"*.{js,ts,jsx,tsx}": ["prettier --write", "eslint --fix"],
"*.{css,yml,json}": ["prettier --write"],
"*.{md}": ["prettier --write", "markdownlint --dot --fix"],
"./Dockerfile": ["dockerfilelint"]
}

8
.prettierignore Normal file
View File

@ -0,0 +1,8 @@
.next
.lighthouseci
node_modules
next-env.d.ts
package.json
package-lock.json
**/workbox-*.js
**/sw.js

6
.prettierrc.json Normal file
View File

@ -0,0 +1,6 @@
{
"singleQuote": true,
"jsxSingleQuote": true,
"semi": false,
"trailingComma": "none"
}

View File

@ -1,11 +1,12 @@
{ {
"recommendations": [ "recommendations": [
"editorconfig.editorconfig",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"divlo.vscode-styled-jsx-syntax", "divlo.vscode-styled-jsx-syntax",
"divlo.vscode-styled-jsx-languageserver", "divlo.vscode-styled-jsx-languageserver",
"bradlc.vscode-tailwindcss", "bradlc.vscode-tailwindcss",
"standard.vscode-standard",
"mikestead.dotenv", "mikestead.dotenv",
"editorconfig.editorconfig",
"coenraads.bracket-pair-colorizer", "coenraads.bracket-pair-colorizer",
"davidanson.vscode-markdownlint" "davidanson.vscode-markdownlint"
] ]

13
.vscode/settings.json vendored
View File

@ -1,8 +1,9 @@
{ {
"standard.enable": true, "typescript.tsdk": "node_modules/typescript/lib",
"standard.engine": "ts-standard", "editor.defaultFormatter": "esbenp.prettier-vscode",
"standard.treatErrorsAsWarnings": true, "prettier.configPath": ".prettierrc.json",
"standard.usePackageJson": true, "editor.formatOnSave": true,
"standard.autoFixOnSave": true, "editor.codeActionsOnSave": {
"typescript.tsdk": "node_modules/typescript/lib" "source.fixAll": true
}
} }

View File

@ -17,15 +17,11 @@ export const FormResult: React.FC<FormResultProps> = (props) => {
if (state === 'loading' || state === 'success') { if (state === 'loading' || state === 'success') {
return ( return (
<FormState state={state}> <FormState state={state}>{t(`home:contact.result.${state}`)}</FormState>
{t(`home:contact.result.${state}`)}
</FormState>
) )
} }
return ( return (
<FormState state='error'> <FormState state='error'>{t(`home:contact.result.${state}`)}</FormState>
{t(`home:contact.result.${state}`)}
</FormState>
) )
} }

View File

@ -5,7 +5,7 @@ export interface FormStateProps extends React.ComponentPropsWithRef<'p'> {
children: string children: string
} }
export const FormState: React.FC<FormStateProps> = props => { export const FormState: React.FC<FormStateProps> = (props) => {
const { state, children, ...rest } = props const { state, children, ...rest } = props
const { t } = useTranslation() const { t } = useTranslation()
@ -15,24 +15,28 @@ export const FormState: React.FC<FormStateProps> = props => {
<p className={state} {...rest}> <p className={state} {...rest}>
{['error', 'success'].includes(state) && ( {['error', 'success'].includes(state) && (
<b> <b>
{state === 'error' ? t('home:contact.error') : t('home:contact.success')}: {state === 'error'
? t('home:contact.error')
: t('home:contact.success')}
:
</b> </b>
)}{' '} )}{' '}
{children} {children}
</p> </p>
</div> </div>
<style jsx>{` <style jsx>
.form-result { {`
margin: 30px; .form-result {
} margin: 30px;
.success { }
color: #90ee90; .success {
} color: #90ee90;
.error { }
color: #ff7f7f; .error {
} color: #ff7f7f;
`} }
`}
</style> </style>
</> </>
) )

View File

@ -6,7 +6,7 @@ export interface ErrorPageProps {
message: string message: string
} }
export const ErrorPage: React.FC<ErrorPageProps> = props => { export const ErrorPage: React.FC<ErrorPageProps> = (props) => {
const { message, statusCode } = props const { message, statusCode } = props
const { t } = useTranslation() const { t } = useTranslation()
@ -19,19 +19,20 @@ export const ErrorPage: React.FC<ErrorPageProps> = props => {
{message} <Link href='/'>{t('errors:returnToHomePage')}</Link> {message} <Link href='/'>{t('errors:returnToHomePage')}</Link>
</p> </p>
<style jsx global>{` <style jsx global>
.content { {`
display: flex; .content {
flex-direction: column; display: flex;
justify-content: center; flex-direction: column;
align-items: center; justify-content: center;
min-width: 100vw; align-items: center;
min-height: 100%; min-width: 100vw;
} min-height: 100%;
#__next { }
padding-top: 0; #__next {
} padding-top: 0;
`} }
`}
</style> </style>
</> </>
) )

View File

@ -7,7 +7,8 @@ export const Footer: React.FC = () => {
<> <>
<footer className='Footer text-center'> <footer className='Footer text-center'>
<p> <p>
<span className='important'>Divlo</span> | {t('common:allRightsReserved')} <span className='important'>Divlo</span> |{' '}
{t('common:allRightsReserved')}
</p> </p>
</footer> </footer>

View File

@ -7,7 +7,7 @@ interface HeadProps {
url?: string url?: string
} }
export const Head: React.FC<HeadProps> = props => { export const Head: React.FC<HeadProps> = (props) => {
const { const {
title = 'Divlo', title = 'Divlo',
image = '/images/icons/icon-96x96.png', image = '/images/icons/icon-96x96.png',

View File

@ -7,7 +7,7 @@ interface InterestItemProps {
fontAwesomeIcon: IconDefinition fontAwesomeIcon: IconDefinition
} }
export const InterestItem: React.FC<InterestItemProps> = props => { export const InterestItem: React.FC<InterestItemProps> = (props) => {
const { fontAwesomeIcon, title } = props const { fontAwesomeIcon, title } = props
return ( return (

View File

@ -6,9 +6,13 @@ import { InterestsList } from './InterestsList'
export const Interests: React.FC = () => { export const Interests: React.FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
const paragraphs: InterestParagraphProps[] = t('home:interests.paragraphs', {}, { const paragraphs: InterestParagraphProps[] = t(
returnObjects: true 'home:interests.paragraphs',
}) {},
{
returnObjects: true
}
)
return ( return (
<> <>

View File

@ -7,7 +7,7 @@ export interface PortfolioItemProps {
image: string image: string
} }
export const PortfolioItem: React.FC<PortfolioItemProps> = props => { export const PortfolioItem: React.FC<PortfolioItemProps> = (props) => {
const { title, description, link, image } = props const { title, description, link, image } = props
return ( return (
@ -32,15 +32,15 @@ export const PortfolioItem: React.FC<PortfolioItemProps> = props => {
<style jsx global> <style jsx global>
{` {`
.portfolio-figure img[alt='${title}'] { .portfolio-figure img[alt='${title}'] {
max-height: 300px; max-height: 300px;
max-width: 300px; max-width: 300px;
transition: opacity 0.5s ease; transition: opacity 0.5s ease;
} }
.portfolio-grid:hover img[alt='${title}'] { .portfolio-grid:hover img[alt='${title}'] {
opacity: 0.05; opacity: 0.05;
} }
`} `}
</style> </style>
<style jsx> <style jsx>

View File

@ -5,9 +5,13 @@ import { PortfolioItem, PortfolioItemProps } from './PortfolioItem'
export const Portfolio: React.FC = () => { export const Portfolio: React.FC = () => {
const { t } = useTranslation('home') const { t } = useTranslation('home')
const items: PortfolioItemProps[] = t('home:portfolio.items', {}, { const items: PortfolioItemProps[] = t(
returnObjects: true 'home:portfolio.items',
}) {},
{
returnObjects: true
}
)
return ( return (
<> <>

View File

@ -4,7 +4,7 @@ interface ProfileItemProps {
link?: string link?: string
} }
export const ProfileItem: React.FC<ProfileItemProps> = props => { export const ProfileItem: React.FC<ProfileItemProps> = (props) => {
const { title, value, link } = props const { title, value, link } = props
return ( return (

View File

@ -8,10 +8,7 @@ export const ProfileList: React.FC = () => {
return ( return (
<> <>
<ul className='profile-list'> <ul className='profile-list'>
<ProfileItem <ProfileItem title={t('home:about.birthDate')} value='31/03/2003' />
title={t('home:about.birthDate')}
value='31/03/2003'
/>
<ProfileItem <ProfileItem
title={t('home:about.nationality')} title={t('home:about.nationality')}
value='Alsace, France' value='Alsace, France'

View File

@ -14,12 +14,13 @@ export const ProfileLogo: React.FC = () => {
</div> </div>
</div> </div>
<style jsx>{` <style jsx>
.profile-logo { {`
margin-right: 10px; .profile-logo {
margin-left: 10px; margin-right: 10px;
} margin-left: 10px;
`} }
`}
</style> </style>
</> </>
) )

View File

@ -6,7 +6,7 @@ interface SocialMediaItemProps {
socialMedia: 'Email' | 'GitHub' | 'Twitch' | 'Twitter' | 'YouTube' socialMedia: 'Email' | 'GitHub' | 'Twitch' | 'Twitter' | 'YouTube'
} }
export const SocialMediaItem: React.FC<SocialMediaItemProps> = props => { export const SocialMediaItem: React.FC<SocialMediaItemProps> = (props) => {
const { link, socialMedia } = props const { link, socialMedia } = props
return ( return (

View File

@ -25,16 +25,17 @@ export const SocialMediaList: React.FC = () => {
</ul> </ul>
</div> </div>
<style jsx>{` <style jsx>
.social-media-list { {`
margin: 0; .social-media-list {
padding: 0; margin: 0;
list-style: none; padding: 0;
text-align: center; list-style: none;
padding: 15px 0; text-align: center;
margin-top: 10px; padding: 15px 0;
} margin-top: 10px;
`} }
`}
</style> </style>
</> </>
) )

View File

@ -6,7 +6,7 @@ export interface SkillProps {
skill: keyof typeof skills skill: keyof typeof skills
} }
export const Skill: React.FC<SkillProps> = props => { export const Skill: React.FC<SkillProps> = (props) => {
const { skill } = props const { skill } = props
const skillProperties = skills[skill] const skillProperties = skills[skill]
@ -29,15 +29,16 @@ export const Skill: React.FC<SkillProps> = props => {
</div> </div>
</a> </a>
<style jsx>{` <style jsx>
.skills-link { {`
max-width: 120px; .skills-link {
margin: 0px 10px 0 10px; max-width: 120px;
} margin: 0px 10px 0 10px;
.skills-text { }
margin-top: 5px; .skills-text {
} margin-top: 5px;
`} }
`}
</style> </style>
</> </>
) )

View File

@ -5,7 +5,7 @@ export interface SkillsSectionProps {
children: React.ReactNode children: React.ReactNode
} }
export const SkillsSection: React.FC<SkillsSectionProps> = props => { export const SkillsSection: React.FC<SkillsSectionProps> = (props) => {
const { title, children } = props const { title, children } = props
return ( return (
@ -23,21 +23,22 @@ export const SkillsSection: React.FC<SkillsSectionProps> = props => {
</div> </div>
</ShadowContainer> </ShadowContainer>
<style jsx>{` <style jsx>
.skills-header { {`
border-bottom: 1px solid rgba(255, 255, 255, 0.1); .skills-header {
margin-bottom: 15px; border-bottom: 1px solid rgba(255, 255, 255, 0.1);
} margin-bottom: 15px;
.skills-header > h3 { }
margin-bottom: 15px; .skills-header > h3 {
} margin-bottom: 15px;
.skills-body { }
display: flex; .skills-body {
justify-content: space-around; display: flex;
flex-flow: row wrap; justify-content: space-around;
padding-top: 1.5rem; flex-flow: row wrap;
} padding-top: 1.5rem;
`} }
`}
</style> </style>
</> </>
) )

View File

@ -16,59 +16,60 @@ export const Input = forwardRef<HTMLInputElement, InputProps>((props, ref) => {
</label> </label>
</div> </div>
<style jsx>{` <style jsx>
.form-group-animation { {`
position: relative; .form-group-animation {
margin-top: 10px; position: relative;
margin-bottom: 30px; margin-top: 10px;
overflow: hidden; margin-bottom: 30px;
} overflow: hidden;
.form-group-animation input { }
width: 100%; .form-group-animation input {
height: 100%; width: 100%;
padding-top: 35px; height: 100%;
color: var(--color-text-1); padding-top: 35px;
border: none; color: var(--color-text-1);
background: transparent; border: none;
outline: none; background: transparent;
} outline: none;
.form-group-animation label { }
position: absolute; .form-group-animation label {
bottom: 0; position: absolute;
left: 0; bottom: 0;
width: 100%; left: 0;
height: 100%; width: 100%;
pointer-events: none; height: 100%;
border-bottom: 1px solid #fff; pointer-events: none;
} border-bottom: 1px solid #fff;
.form-group-animation label::after { }
content: ''; .form-group-animation label::after {
position: absolute; content: '';
left: 0; position: absolute;
bottom: -1px; left: 0;
height: 100%; bottom: -1px;
width: 100%; height: 100%;
border-bottom: 3px solid var(--color-primary); width: 100%;
transform: translateX(-100%); border-bottom: 3px solid var(--color-primary);
transition: transform 0.2s ease; transform: translateX(-100%);
} transition: transform 0.2s ease;
.label-content { }
position: absolute; .label-content {
bottom: 5px; position: absolute;
left: 0px; bottom: 5px;
transition: all 0.3s ease; left: 0px;
} transition: all 0.3s ease;
.form-group-animation input:focus + .label .label-content, }
.form-group-animation input:valid + .label .label-content { .form-group-animation input:focus + .label .label-content,
transform: translateY(-150%); .form-group-animation input:valid + .label .label-content {
font-size: 14px; transform: translateY(-150%);
color: var(--color-primary); font-size: 14px;
} color: var(--color-primary);
.form-group-animation input:focus + .label::after, }
.form-group-animation input:valid + .label::after { .form-group-animation input:focus + .label::after,
transform: translateX(0%); .form-group-animation input:valid + .label::after {
} transform: translateX(0%);
`} }
`}
</style> </style>
</> </>
) )

View File

@ -1,6 +1,6 @@
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
export const RevealFade: React.FC = props => { export const RevealFade: React.FC = (props) => {
const { children } = props const { children } = props
const htmlElement = useRef<HTMLDivElement>(null) const htmlElement = useRef<HTMLDivElement>(null)
@ -8,7 +8,7 @@ export const RevealFade: React.FC = props => {
useEffect(() => { useEffect(() => {
const observer = new window.IntersectionObserver( const observer = new window.IntersectionObserver(
(entries, observer) => { (entries, observer) => {
entries.forEach(entry => { entries.forEach((entry) => {
if (entry.isIntersecting) { if (entry.isIntersecting) {
entry.target.classList.add('reveal-visible') entry.target.classList.add('reveal-visible')
observer.unobserve(entry.target) observer.unobserve(entry.target)
@ -30,19 +30,20 @@ export const RevealFade: React.FC = props => {
{children} {children}
</div> </div>
<style jsx>{` <style jsx>
.reveal { {`
opacity: 0; .reveal {
visibility: hidden; opacity: 0;
transform: translateY(-30px); visibility: hidden;
} transform: translateY(-30px);
.reveal-visible { }
opacity: 1; .reveal-visible {
visibility: visible; opacity: 1;
transform: translateY(0); visibility: visible;
transition: all 500ms ease-out 100ms; transform: translateY(0);
} transition: all 500ms ease-out 100ms;
`} }
`}
</style> </style>
</> </>
) )

View File

@ -3,8 +3,8 @@ import { forwardRef } from 'react'
type SectionHeadingProps = React.ComponentPropsWithRef<'h2'> type SectionHeadingProps = React.ComponentPropsWithRef<'h2'>
export const SectionHeading = forwardRef< export const SectionHeading = forwardRef<
HTMLHeadingElement, HTMLHeadingElement,
SectionHeadingProps SectionHeadingProps
>((props, ref) => { >((props, ref) => {
const { children, ...rest } = props const { children, ...rest } = props

View File

@ -1,6 +1,6 @@
type ShadowContainerProps = React.ComponentPropsWithRef<'div'> type ShadowContainerProps = React.ComponentPropsWithRef<'div'>
export const ShadowContainer: React.FC<ShadowContainerProps> = props => { export const ShadowContainer: React.FC<ShadowContainerProps> = (props) => {
const { children, className, ...rest } = props const { children, className, ...rest } = props
return ( return (

View File

@ -16,22 +16,23 @@ export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
<textarea id={name} name={name} ref={ref} {...rest} /> <textarea id={name} name={name} ref={ref} {...rest} />
</div> </div>
<style jsx>{` <style jsx>
.form-group { {`
padding-top: 15px; .form-group {
margin-bottom: 30px; padding-top: 15px;
} margin-bottom: 30px;
.form-group textarea { }
background: transparent; .form-group textarea {
color: var(--color-text); background: transparent;
outline: none; color: var(--color-text);
width: 100%; outline: none;
height: auto; width: 100%;
padding: 10px; height: auto;
resize: vertical; padding: 10px;
margin-top: 8px; resize: vertical;
} margin-top: 8px;
`} }
`}
</style> </style>
</> </>
) )

View File

@ -3,7 +3,7 @@ interface TooltipProps extends React.ComponentPropsWithRef<'div'> {
children: React.ReactNode children: React.ReactNode
} }
export const Tooltip: React.FC<TooltipProps> = props => { export const Tooltip: React.FC<TooltipProps> = (props) => {
const { title, children, ...rest } = props const { title, children, ...rest } = props
return ( return (
<> <>
@ -12,37 +12,38 @@ export const Tooltip: React.FC<TooltipProps> = props => {
<span className='title'>{title}</span> <span className='title'>{title}</span>
</span> </span>
<style jsx>{` <style jsx>
.title { {`
color: #fff; .title {
font-size: 11px; color: #fff;
font-weight: 400; font-size: 11px;
line-height: 1; font-weight: 400;
display: inline-block; line-height: 1;
background-color: #222222; display: inline-block;
padding: 5px 8px; background-color: #222222;
white-space: nowrap; padding: 5px 8px;
position: absolute; white-space: nowrap;
top: 100%; position: absolute;
margin-top: 10px; top: 100%;
z-index: 1; margin-top: 10px;
opacity: 0; z-index: 1;
visibility: hidden; opacity: 0;
border-radius: 3px; visibility: hidden;
transition: all 0.15s ease-in; border-radius: 3px;
transform: translate3d(0, -15px, 0); transition: all 0.15s ease-in;
backface-visibility: hidden; transform: translate3d(0, -15px, 0);
} backface-visibility: hidden;
.tooltip ~ .tooltip:hover .title, }
.tooltip:first-child:hover .title { .tooltip ~ .tooltip:hover .title,
opacity: 1; .tooltip:first-child:hover .title {
visibility: visible; opacity: 1;
transition: all 0.35s ease-out; visibility: visible;
transform: translate3d(0, 0, 0); transition: all 0.35s ease-out;
margin: 0; transform: translate3d(0, 0, 0);
backface-visibility: hidden; margin: 0;
} backface-visibility: hidden;
`} }
`}
</style> </style>
</> </>
) )

2166
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,22 +6,6 @@
"type": "git", "type": "git",
"url": "https://github.com/Divlo/Divlo" "url": "https://github.com/Divlo/Divlo"
}, },
"ts-standard": {
"ignore": [
".next",
".lighthouseci",
"node_modules",
"next-env.d.ts",
"**/workbox-*.js",
"**/sw.js"
],
"envs": [
"node",
"browser",
"jest"
],
"report": "stylish"
},
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",
"start": "next start", "start": "next start",
@ -30,8 +14,9 @@
"lint:commit": "commitlint", "lint:commit": "commitlint",
"lint:docker": "dockerfilelint './Dockerfile'", "lint:docker": "dockerfilelint './Dockerfile'",
"lint:editorconfig": "editorconfig-checker", "lint:editorconfig": "editorconfig-checker",
"lint:markdown": "markdownlint '**/*.md' --dot --ignore node_modules", "lint:markdown": "markdownlint '*.md' --dot --ignore node_modules",
"lint:typescript": "ts-standard", "lint:typescript": "eslint '*.{js,ts,jsx,tsx}'",
"lint:staged": "lint-staged",
"lighthouse": "lhci autorun", "lighthouse": "lhci autorun",
"test": "jest", "test": "jest",
"release": "semantic-release", "release": "semantic-release",
@ -70,18 +55,27 @@
"@types/react": "17.0.4", "@types/react": "17.0.4",
"@types/styled-jsx": "2.2.8", "@types/styled-jsx": "2.2.8",
"@types/validator": "13.1.3", "@types/validator": "13.1.3",
"@typescript-eslint/eslint-plugin": "4.22.0",
"autoprefixer": "10.2.5", "autoprefixer": "10.2.5",
"babel-jest": "26.6.3", "babel-jest": "26.6.3",
"dockerfilelint": "1.8.0", "dockerfilelint": "1.8.0",
"editorconfig-checker": "4.0.2", "editorconfig-checker": "4.0.2",
"eslint": "7.25.0",
"eslint-config-prettier": "8.3.0",
"eslint-config-standard-with-typescript": "20.0.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-node": "11.1.0",
"eslint-plugin-prettier": "3.4.0",
"eslint-plugin-promise": "4.3.1",
"husky": "6.0.0", "husky": "6.0.0",
"jest": "26.6.3", "jest": "26.6.3",
"lint-staged": "10.5.4",
"markdownlint-cli": "0.27.1", "markdownlint-cli": "0.27.1",
"node-mocks-http": "1.10.1", "node-mocks-http": "1.10.1",
"postcss": "8.2.13", "postcss": "8.2.13",
"prettier": "2.2.1",
"semantic-release": "17.4.2", "semantic-release": "17.4.2",
"tailwindcss": "2.1.2", "tailwindcss": "2.1.2",
"ts-standard": "10.0.0",
"typescript": "4.2.4" "typescript": "4.2.4"
} }
} }

View File

@ -8,14 +8,14 @@ import Document, {
} from 'next/document' } from 'next/document'
class MyDocument extends Document { class MyDocument extends Document {
static async getInitialProps ( static async getInitialProps(
ctx: DocumentContext ctx: DocumentContext
): Promise<DocumentInitialProps> { ): Promise<DocumentInitialProps> {
const initialProps = await Document.getInitialProps(ctx) const initialProps = await Document.getInitialProps(ctx)
return initialProps return initialProps
} }
render (): JSX.Element { render(): JSX.Element {
return ( return (
<Html> <Html>
<Head /> <Head />

View File

@ -30,7 +30,11 @@ const Home: React.FC = () => {
</RevealFade> </RevealFade>
<RevealFade> <RevealFade>
<Section id='skills' heading={t('home:skills.title')} withoutShadowContainer> <Section
id='skills'
heading={t('home:skills.title')}
withoutShadowContainer
>
<Skills /> <Skills />
</Section> </Section>
</RevealFade> </RevealFade>