├── .eslintrc.json
├── .gitignore
├── .webflowrc.js
├── README.md
├── components
├── Chart.js
└── index.js
├── devlink
├── AboutHero.d.ts
├── AboutHero.js
├── AboutHero.module.css
├── AboutInformation.d.ts
├── AboutInformation.js
├── AboutInformation.module.css
├── Brands.d.ts
├── Brands.js
├── Brands.module.css
├── Cta.d.ts
├── Cta.js
├── Cta.module.css
├── Details.d.ts
├── Details.js
├── Details.module.css
├── Features.d.ts
├── Features.js
├── Features.module.css
├── Footer.d.ts
├── Footer.js
├── Footer.module.css
├── Hero.d.ts
├── Hero.js
├── Hero.module.css
├── JobListing.d.ts
├── JobListing.js
├── JobListing.module.css
├── Navbar.d.ts
├── Navbar.js
├── Navbar.module.css
├── Newnav.d.ts
├── Newnav.js
├── Newnav.module.css
├── PageHeading.d.ts
├── PageHeading.js
├── PageHeading.module.css
├── Pricing.d.ts
├── Pricing.js
├── Pricing.module.css
├── PricingGrid.d.ts
├── PricingGrid.js
├── PricingGrid.module.css
├── Stats.d.ts
├── Stats.js
├── Stats.module.css
├── _Builtin
│ ├── BackgroundVideo.d.ts
│ ├── BackgroundVideo.js
│ ├── Basic.d.ts
│ ├── Basic.js
│ ├── Dropdown.d.ts
│ ├── Dropdown.js
│ ├── Facebook.d.ts
│ ├── Facebook.js
│ ├── Form.d.ts
│ ├── Form.js
│ ├── Map.d.ts
│ ├── Map.js
│ ├── Navbar.d.ts
│ ├── Navbar.js
│ ├── Slider.d.ts
│ ├── Slider.js
│ ├── Tabs.d.ts
│ ├── Tabs.js
│ ├── Twitter.d.ts
│ ├── Twitter.js
│ ├── Typography.d.ts
│ ├── Typography.js
│ ├── Video.d.ts
│ ├── Video.js
│ ├── YouTubeVideo.d.ts
│ ├── YouTubeVideo.js
│ ├── index.d.ts
│ └── index.js
├── devlink.d.ts
├── devlink.js
├── global.css
├── index.d.ts
├── index.js
├── interactions.d.ts
├── interactions.js
├── types.d.ts
├── types.js
├── utils.d.ts
└── utils.js
├── fetchJob.js
├── next.config.js
├── package-lock.json
├── package.json
├── pages
├── _app.tsx
├── _document.tsx
├── about.tsx
├── api
│ ├── jobs.js
│ └── jobs
│ │ ├── [id].js
│ │ └── featured.js
├── index.tsx
├── jobs.tsx
└── jobs
│ └── [id].jsx
├── public
├── favicon.ico
├── next.svg
└── vercel.svg
├── styles
├── Home.module.css
└── globals.css
└── tsconfig.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | .pnpm-debug.log*
27 |
28 | # local env files
29 | .env*.local
30 | *.env
31 | .webflowrc.json
32 |
33 | # vercel
34 | .vercel
35 |
36 | # typescript
37 | *.tsbuildinfo
38 | next-env.d.ts
39 |
--------------------------------------------------------------------------------
/.webflowrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | host: "https://api.webflow.com",
3 | rootDir: "./devlink",
4 | siteId: process.env.WF_SITE_ID,
5 | authToken: process.env.WF_SITE_TOKEN, // An environment variable is recommended for this field.
6 | cssModules: true,
7 | };
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) and developed using [Webflow](https://webflow.com) and [DevLink](https://webflow.com/devlink).
2 |
3 | ## Webflow Cloneable
4 |
5 | You can clone the Webflow project used with this Next JS project at:
6 |
7 | [https://webflow.com/made-in-webflow/website/job-board-dl](https://webflow.com/made-in-webflow/website/job-board-dl)
8 |
9 | Make a copy, and then download this repo and connect the two using DevLink.
10 |
11 | Here's a Loom video to walk you through the process:
12 |
13 | [https://www.loom.com/share/ea21f62201df4e60a1f92524a28f810e](https://www.loom.com/share/ea21f62201df4e60a1f92524a28f810e)
14 |
15 | You can also view [our DevLink documentation](https://docs.developers.webflow.com/docs/devlink-documentation-and-usage-guide) to learn more about all the options, features, and supported elements.
16 |
17 | ## Getting Started
18 |
19 | To get started, run the development server:
20 |
21 | ```bash
22 | npm run dev
23 | # or
24 | yarn dev
25 | # or
26 | pnpm dev
27 | ```
28 |
29 | ## Live site
30 |
31 | Open [http://localhost:3000](http://localhost:3000) with your browser to view the site.
32 |
33 | ## Backend and environment variables
34 |
35 | We created a light weight backend to serve up the content from Airtable. In order for this to work, you'll need to get a copy of [the Airtable](https://airtable.com/shr7X55pL1X4yDXq7), generate a [personal access token](https://airtable.com/developers/web/guides/personal-access-tokens) in Airtable (I used the scopes `data.records:read` and `schema.records.read`) and then in the root of your project create a `.env` file locally with the lines:
36 |
37 | ```
38 | JOBS_KEY=YOURPERSONALACCESSTOKENHERE
39 | AIRTABLE_BASE_ID=YOURPERSONALAIRTABLEBASID
40 | WF_SITE_ID=YOURPERSONALWEBFLOWSITEID
41 | WF_SITE_TOKEN=YOURPERSONALWEBFLOWSITETOKEN
42 | ```
43 |
44 | Here you can add:
45 |
46 | - Your Airtable API key
47 | - The ID of your Airtable base
48 | - Your Webflow API key
49 | - Your Webflow Site ID
50 |
51 | Once you've added these to your `.env` file, this project should function as expected.
--------------------------------------------------------------------------------
/components/Chart.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Chart as ChartJS, ArcElement, Tooltip, Legend } from "chart.js";
3 | import { Pie } from "react-chartjs-2";
4 |
5 | ChartJS.register(ArcElement, Tooltip, Legend);
6 |
7 | const data = {
8 | labels: ["More than 7 days", "Less than a week"],
9 | datasets: [
10 | {
11 | label: "% of open roles",
12 | data: [15, 85],
13 | backgroundColor: ["rgba(153, 102, 255, 0.2)", "rgba(54, 162, 235, 0.2)"],
14 | borderColor: ["rgba(153, 102, 255, 1)", "rgba(54, 162, 235, 1)"],
15 | borderWidth: 1,
16 | },
17 | ],
18 | };
19 |
20 | export function Chart() {
21 | return (
22 |
33 | );
34 | }
35 |
--------------------------------------------------------------------------------
/components/index.js:
--------------------------------------------------------------------------------
1 | export * from "./Chart";
2 |
--------------------------------------------------------------------------------
/devlink/AboutHero.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | declare function AboutHero(props: {
4 | as?: React.ElementType;
5 | }): React.JSX.Element;
6 |
--------------------------------------------------------------------------------
/devlink/AboutHero.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _utils from "./utils";
4 | import _styles from "./AboutHero.module.css";
5 |
6 | export function AboutHero({ as: _Component = _Builtin.Section }) {
7 | return (
8 | <_Component className={_utils.cx(_styles, "section", "hero")} tag="section">
9 | <_Builtin.Container className={_utils.cx(_styles, "container")} tag="div">
10 | <_Builtin.Block className={_utils.cx(_styles, "two-columns")} tag="div">
11 | <_Builtin.Block
12 | className={_utils.cx(_styles, "hero-content-wrapper")}
13 | id={_utils.cx(
14 | _styles,
15 | "w-node-e99518a0-9081-2bc9-a4b2-c23fb0c1b414-b0c1b411"
16 | )}
17 | tag="div"
18 | >
19 | <_Builtin.Heading
20 | className={_utils.cx(_styles, "hero-heading")}
21 | tag="h1"
22 | >
23 | {"About us"}
24 |
25 | <_Builtin.Paragraph
26 | className={_utils.cx(_styles, "hero-paragraph")}
27 | >
28 | {
29 | "Discover the Future of Development at the Visual Developers Job Board - Where Talent Meets Opportunity."
30 | }
31 |
32 |
33 | <_Builtin.Block
34 | className={_utils.cx(_styles, "hero-image-wrapper")}
35 | id={_utils.cx(
36 | _styles,
37 | "w-node-e99518a0-9081-2bc9-a4b2-c23fb0c1b419-b0c1b411"
38 | )}
39 | tag="div"
40 | >
41 | <_Builtin.Image
42 | className={_utils.cx(_styles, "hero-image")}
43 | loading="lazy"
44 | width="auto"
45 | height="auto"
46 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/644ea5920aef435a232ffdfc_Other%2021.png"
47 | />
48 |
49 |
50 |
51 |
52 | );
53 | }
54 |
--------------------------------------------------------------------------------
/devlink/AboutHero.module.css:
--------------------------------------------------------------------------------
1 | .section {
2 | padding-top: 5em;
3 | padding-bottom: 5em;
4 | }
5 |
6 | .section.hero {
7 | position: relative;
8 | min-height: 600px;
9 | background-color: var(--clrPrimary600-aeaf7110);
10 | color: var(--clrNeutral100-f9191126);
11 | }
12 |
13 | .section.title {
14 | background-color: var(--clrPrimary400-6c00fdf1);
15 | color: var(--clrPrimary999-b0a60ce1);
16 | }
17 |
18 | .section.pricing-gradient {
19 | background-image: radial-gradient(circle farthest-corner at 50% 100%, var(--clrPrimary400-6c00fdf1), var(--clrPrimary999-b0a60ce1) 73%);
20 | color: var(--clrNeutral100-f9191126);
21 | }
22 |
23 | .section.pricing {
24 | position: relative;
25 | padding-top: 5em;
26 | color: hsla(329.9999999999999, 0.00%, 100.00%, 1.00);
27 | text-align: center;
28 | }
29 |
30 | .clr-secondary-400 {
31 | background-color: var(--clrSecondary400-685c3959);
32 | }
33 |
34 | .clr-primary-400 {
35 | background-color: var(--clrPrimary400-6c00fdf1);
36 | }
37 |
38 | .section.features {
39 | color: var(--clrPrimary600-aeaf7110);
40 | }
41 |
42 | .container {
43 | width: 100%;
44 | max-width: 1200px;
45 | margin-right: auto;
46 | margin-left: auto;
47 | padding-right: 1em;
48 | padding-left: 1em;
49 | }
50 |
51 | @media screen and (max-width: 767px) {
52 | .container {
53 | padding-right: 1.5em;
54 | padding-left: 1.5em;
55 | }
56 | }
57 |
58 | .container.nav {
59 | display: flex;
60 | justify-content: space-between;
61 | align-items: center;
62 | }
63 |
64 | .container.narrow {
65 | max-width: 720px;
66 | }
67 |
68 | .container.rel {
69 | position: relative;
70 | z-index: 2;
71 | }
72 |
73 | .center {
74 | text-align: center;
75 | }
76 |
77 | .two-columns {
78 | display: grid;
79 | padding-left: 0px;
80 | grid-auto-columns: 1fr;
81 | grid-column-gap: 1.5rem;
82 | grid-row-gap: 1.5rem;
83 | grid-template-columns: 1fr 1fr;
84 | grid-template-rows: auto;
85 | }
86 |
87 | @media screen and (max-width: 767px) {
88 | .two-columns {
89 | grid-template-columns: 1fr;
90 | }
91 | }
92 |
93 | .two-columns.narrow {
94 | max-width: 960px;
95 | margin: 3em auto;
96 | }
97 |
98 | .hero-content-wrapper {
99 | display: flex;
100 | flex-direction: column;
101 | flex-wrap: nowrap;
102 | align-items: flex-start;
103 | grid-row-gap: 1.2em;
104 | }
105 |
106 | .hero-heading {
107 | margin-top: 0px;
108 | margin-bottom: 0px;
109 |
110 | font-family: 'Playfair Display';
111 | font-size: 5rem;
112 | line-height: 1;
113 | font-weight: 700;
114 | text-align: left;
115 | -webkit-text-stroke-color: var(--clrPrimary600-aeaf7110);
116 | }
117 |
118 | @media screen and (max-width: 991px) {
119 | .hero-heading {
120 | font-size: 5rem;
121 | }
122 | }
123 |
124 | @media screen and (max-width: 479px) {
125 | .hero-heading {
126 | line-height: 1;
127 | }
128 | }
129 |
130 | @media screen and (max-width: 767px) {
131 | .hero-heading {
132 | font-size: 14vw;
133 | }
134 | }
135 |
136 | .hero-paragraph {
137 | max-width: 60ch;
138 | margin-bottom: 0.5em;
139 | font-size: 1.2rem;
140 | }
141 |
142 | .hero-image {
143 | width: 100%;
144 | height: 100%;
145 | object-fit: contain;
146 | }
147 |
148 | #w-node-e99518a0-9081-2bc9-a4b2-c23fb0c1b414-b0c1b411 {
149 | grid-column-start: span 1;
150 | grid-column-end: span 1;
151 | grid-row-start: span 1;
152 | grid-row-end: span 1;
153 | align-self: center;
154 | }
155 | #w-node-e99518a0-9081-2bc9-a4b2-c23fb0c1b419-b0c1b411 {
156 | grid-column-start: span 1;
157 | grid-column-end: span 1;
158 | grid-row-start: span 1;
159 | grid-row-end: span 1;
160 | }
--------------------------------------------------------------------------------
/devlink/AboutInformation.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | declare function AboutInformation(props: {
4 | as?: React.ElementType;
5 | }): React.JSX.Element;
6 |
--------------------------------------------------------------------------------
/devlink/AboutInformation.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _utils from "./utils";
4 | import _styles from "./AboutInformation.module.css";
5 |
6 | export function AboutInformation({ as: _Component = _Builtin.Section }) {
7 | return (
8 | <_Component className={_utils.cx(_styles, "section")} tag="div">
9 | <_Builtin.Container
10 | className={_utils.cx(_styles, "container", "narrow")}
11 | tag="div"
12 | >
13 | <_Builtin.RichText tag="div">
14 | <_Builtin.Paragraph>
15 | {
16 | "The Visual Developers Job Board was created with a simple mission: to bridge the gap between the most talented developers in the Webflow, no-code, and traditional development spaces and the forward-thinking companies that need their expertise. Our platform offers a curated selection of high-quality job listings from innovative organizations that understand the importance of blending creativity and technical acumen. We're more than just a job board - we're a community that fosters growth and collaboration, helping our users unlock their true potential and reshape the digital landscape. Join us today and be a part of the revolution in modern development."
17 | }
18 |
19 | <_Builtin.Paragraph>
20 | {
21 | "In an ever-evolving technological landscape, the Visual Developers Job Board was born out of a passion for driving innovation and embracing the transformative potential of modern development techniques. We recognized the need for a dedicated platform that caters to Webflow, no-code, and traditional developers, as well as the companies that rely on their unique skillsets. Our commitment to fostering a vibrant community, centered around knowledge-sharing and collaboration, is what sets us apart. We exist to empower developers by providing them with the tools, resources, and opportunities needed to excel, while helping companies discover exceptional talent to bring their visionary projects to life. Together, we're building a more connected, creative, and technologically advanced future."
22 | }
23 |
24 |
25 |
26 |
27 | );
28 | }
29 |
--------------------------------------------------------------------------------
/devlink/AboutInformation.module.css:
--------------------------------------------------------------------------------
1 | .section {
2 | padding-top: 5em;
3 | padding-bottom: 5em;
4 | }
5 |
6 | .section.hero {
7 | position: relative;
8 | min-height: 600px;
9 | background-color: var(--clrPrimary600-aeaf7110);
10 | color: var(--clrNeutral100-f9191126);
11 | }
12 |
13 | .section.title {
14 | background-color: var(--clrPrimary400-6c00fdf1);
15 | color: var(--clrPrimary999-b0a60ce1);
16 | }
17 |
18 | .section.pricing-gradient {
19 | background-image: radial-gradient(circle farthest-corner at 50% 100%, var(--clrPrimary400-6c00fdf1), var(--clrPrimary999-b0a60ce1) 73%);
20 | color: var(--clrNeutral100-f9191126);
21 | }
22 |
23 | .section.pricing {
24 | position: relative;
25 | padding-top: 5em;
26 | color: hsla(329.9999999999999, 0.00%, 100.00%, 1.00);
27 | text-align: center;
28 | }
29 |
30 | .clr-secondary-400 {
31 | background-color: var(--clrSecondary400-685c3959);
32 | }
33 |
34 | .clr-primary-400 {
35 | background-color: var(--clrPrimary400-6c00fdf1);
36 | }
37 |
38 | .section.features {
39 | color: var(--clrPrimary600-aeaf7110);
40 | }
41 |
42 | .container {
43 | width: 100%;
44 | max-width: 1200px;
45 | margin-right: auto;
46 | margin-left: auto;
47 | padding-right: 1em;
48 | padding-left: 1em;
49 | }
50 |
51 | @media screen and (max-width: 767px) {
52 | .container {
53 | padding-right: 1.5em;
54 | padding-left: 1.5em;
55 | }
56 | }
57 |
58 | .container.nav {
59 | display: flex;
60 | justify-content: space-between;
61 | align-items: center;
62 | }
63 |
64 | .container.narrow {
65 | max-width: 720px;
66 | }
67 |
68 | .container.rel {
69 | position: relative;
70 | z-index: 2;
71 | }
72 |
73 | .center {
74 | text-align: center;
75 | }
--------------------------------------------------------------------------------
/devlink/Brands.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | declare function Brands(props: { as?: React.ElementType }): React.JSX.Element;
4 |
--------------------------------------------------------------------------------
/devlink/Brands.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _utils from "./utils";
4 | import _styles from "./Brands.module.css";
5 |
6 | export function Brands({ as: _Component = _Builtin.Section }) {
7 | return (
8 | <_Component className={_utils.cx(_styles, "section")} tag="section">
9 | <_Builtin.Container className={_utils.cx(_styles, "container")} tag="div">
10 | <_Builtin.Heading
11 | className={_utils.cx(_styles, "brand-heading")}
12 | tag="h2"
13 | >
14 | {"Trusted by brands around the world"}
15 |
16 | <_Builtin.Block className={_utils.cx(_styles, "logo-row")} tag="div">
17 | <_Builtin.Block
18 | className={_utils.cx(_styles, "logo-wrapper")}
19 | id={_utils.cx(
20 | _styles,
21 | "w-node-b77115e0-1822-f0f7-10e0-565207c3febe-07c3feb9"
22 | )}
23 | tag="div"
24 | >
25 | <_Builtin.Image
26 | className={_utils.cx(_styles, "logo")}
27 | id={_utils.cx(
28 | _styles,
29 | "w-node-b77115e0-1822-f0f7-10e0-565207c3febf-07c3feb9"
30 | )}
31 | loading="lazy"
32 | width="auto"
33 | height="auto"
34 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/644eaaf30704eaa87b2ce740_logo1.svg"
35 | />
36 |
37 | <_Builtin.Block
38 | className={_utils.cx(_styles, "logo-wrapper")}
39 | id={_utils.cx(
40 | _styles,
41 | "w-node-b77115e0-1822-f0f7-10e0-565207c3fec0-07c3feb9"
42 | )}
43 | tag="div"
44 | >
45 | <_Builtin.Image
46 | className={_utils.cx(_styles, "logo")}
47 | loading="lazy"
48 | width="auto"
49 | height="auto"
50 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/644eaaf349fbba209f989dd9_logo2.svg"
51 | />
52 |
53 | <_Builtin.Block
54 | className={_utils.cx(_styles, "logo-wrapper")}
55 | id={_utils.cx(
56 | _styles,
57 | "w-node-b77115e0-1822-f0f7-10e0-565207c3fec2-07c3feb9"
58 | )}
59 | tag="div"
60 | >
61 | <_Builtin.Image
62 | className={_utils.cx(_styles, "logo")}
63 | id={_utils.cx(
64 | _styles,
65 | "w-node-b77115e0-1822-f0f7-10e0-565207c3fec3-07c3feb9"
66 | )}
67 | loading="lazy"
68 | width="auto"
69 | height="auto"
70 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/644eaaf34c163508f9a43e72_logo3.svg"
71 | />
72 |
73 | <_Builtin.Block
74 | className={_utils.cx(_styles, "logo-wrapper")}
75 | id={_utils.cx(
76 | _styles,
77 | "w-node-b77115e0-1822-f0f7-10e0-565207c3fec4-07c3feb9"
78 | )}
79 | tag="div"
80 | >
81 | <_Builtin.Image
82 | className={_utils.cx(_styles, "logo")}
83 | loading="lazy"
84 | width="auto"
85 | height="auto"
86 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/644eaaf3e4f01820ea833689_logo5.svg"
87 | />
88 |
89 | <_Builtin.Block
90 | className={_utils.cx(_styles, "logo-wrapper")}
91 | id={_utils.cx(
92 | _styles,
93 | "w-node-b77115e0-1822-f0f7-10e0-565207c3fec6-07c3feb9"
94 | )}
95 | tag="div"
96 | >
97 | <_Builtin.Image
98 | className={_utils.cx(_styles, "logo")}
99 | loading="lazy"
100 | width="auto"
101 | height="auto"
102 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/644eaaf3c76d49471f3602f3_logo4.svg"
103 | />
104 |
105 |
106 |
107 |
108 | );
109 | }
110 |
--------------------------------------------------------------------------------
/devlink/Brands.module.css:
--------------------------------------------------------------------------------
1 | .section {
2 | padding-top: 5em;
3 | padding-bottom: 5em;
4 | }
5 |
6 | .section.hero {
7 | position: relative;
8 | min-height: 600px;
9 | background-color: var(--clrPrimary600-aeaf7110);
10 | color: var(--clrNeutral100-f9191126);
11 | }
12 |
13 | .section.title {
14 | background-color: var(--clrPrimary400-6c00fdf1);
15 | color: var(--clrPrimary999-b0a60ce1);
16 | }
17 |
18 | .section.pricing-gradient {
19 | background-image: radial-gradient(circle farthest-corner at 50% 100%, var(--clrPrimary400-6c00fdf1), var(--clrPrimary999-b0a60ce1) 73%);
20 | color: var(--clrNeutral100-f9191126);
21 | }
22 |
23 | .section.pricing {
24 | position: relative;
25 | padding-top: 5em;
26 | color: hsla(329.9999999999999, 0.00%, 100.00%, 1.00);
27 | text-align: center;
28 | }
29 |
30 | .clr-secondary-400 {
31 | background-color: var(--clrSecondary400-685c3959);
32 | }
33 |
34 | .clr-primary-400 {
35 | background-color: var(--clrPrimary400-6c00fdf1);
36 | }
37 |
38 | .section.features {
39 | color: var(--clrPrimary600-aeaf7110);
40 | }
41 |
42 | .container {
43 | width: 100%;
44 | max-width: 1200px;
45 | margin-right: auto;
46 | margin-left: auto;
47 | padding-right: 1em;
48 | padding-left: 1em;
49 | }
50 |
51 | @media screen and (max-width: 767px) {
52 | .container {
53 | padding-right: 1.5em;
54 | padding-left: 1.5em;
55 | }
56 | }
57 |
58 | .container.nav {
59 | display: flex;
60 | justify-content: space-between;
61 | align-items: center;
62 | }
63 |
64 | .container.narrow {
65 | max-width: 720px;
66 | }
67 |
68 | .container.rel {
69 | position: relative;
70 | z-index: 2;
71 | }
72 |
73 | .center {
74 | text-align: center;
75 | }
76 |
77 | .brand-heading {
78 | margin-bottom: 2em;
79 | color: var(--clrPrimary600-aeaf7110);
80 | text-align: center;
81 | }
82 |
83 | .logo-row {
84 | display: flex;
85 | grid-auto-columns: 1fr;
86 | grid-column-gap: 4em;
87 | grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
88 | grid-template-rows: auto;
89 | }
90 |
91 | @media screen and (max-width: 767px) {
92 | .logo-row {
93 | justify-content: center;
94 | flex-wrap: wrap;
95 | align-items: center;
96 | grid-column-gap: 1em;
97 | grid-row-gap: 1em;
98 | }
99 | }
100 |
101 | @media screen and (max-width: 479px) {
102 | .logo-row {
103 | grid-row-gap: 1em;
104 | grid-template-columns: 1fr 1fr 1fr;
105 | }
106 | }
107 |
108 | .logo-wrapper {
109 | color: var(--clrPrimary600-aeaf7110);
110 | }
111 |
112 | @media screen and (max-width: 767px) {
113 | .logo-wrapper {
114 | width: 24%;
115 | }
116 | }
117 |
118 | @media screen and (max-width: 479px) {
119 | .logo-wrapper {
120 | width: 40%;
121 | }
122 | }
123 |
124 | .logo {
125 | width: 100%;
126 | height: 100%;
127 | object-fit: contain;
128 | }
129 |
130 | #w-node-b77115e0-1822-f0f7-10e0-565207c3febe-07c3feb9 {
131 | grid-column-start: span 1;
132 | grid-column-end: span 1;
133 | grid-row-start: span 1;
134 | grid-row-end: span 1;
135 | align-self: center;
136 | }
137 | #w-node-b77115e0-1822-f0f7-10e0-565207c3febf-07c3feb9 {
138 | grid-column-start: span 1;
139 | grid-column-end: span 1;
140 | grid-row-start: span 1;
141 | grid-row-end: span 1;
142 | }
143 | #w-node-b77115e0-1822-f0f7-10e0-565207c3fec0-07c3feb9 {
144 | grid-column-start: span 1;
145 | grid-column-end: span 1;
146 | grid-row-start: span 1;
147 | grid-row-end: span 1;
148 | align-self: center;
149 | }
150 | #w-node-b77115e0-1822-f0f7-10e0-565207c3fec2-07c3feb9 {
151 | grid-column-start: span 1;
152 | grid-column-end: span 1;
153 | grid-row-start: span 1;
154 | grid-row-end: span 1;
155 | align-self: center;
156 | }
157 | #w-node-b77115e0-1822-f0f7-10e0-565207c3fec3-07c3feb9 {
158 | grid-column-start: span 1;
159 | grid-column-end: span 1;
160 | grid-row-start: span 1;
161 | grid-row-end: span 1;
162 | }
163 | #w-node-b77115e0-1822-f0f7-10e0-565207c3fec4-07c3feb9 {
164 | grid-column-start: span 1;
165 | grid-column-end: span 1;
166 | grid-row-start: span 1;
167 | grid-row-end: span 1;
168 | align-self: center;
169 | }
170 | #w-node-b77115e0-1822-f0f7-10e0-565207c3fec6-07c3feb9 {
171 | grid-column-start: span 1;
172 | grid-column-end: span 1;
173 | grid-row-start: span 1;
174 | grid-row-end: span 1;
175 | align-self: center;
176 | }
--------------------------------------------------------------------------------
/devlink/Cta.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as Types from "./types";
3 |
4 | declare function Cta(props: {
5 | as?: React.ElementType;
6 | headingText?: React.ReactNode;
7 | button?: Types.Devlink.RuntimeProps;
8 | }): React.JSX.Element;
9 |
--------------------------------------------------------------------------------
/devlink/Cta.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _utils from "./utils";
4 | import _styles from "./Cta.module.css";
5 |
6 | export function Cta({
7 | as: _Component = _Builtin.Section,
8 | headingText = "Find your next job, today",
9 | button = {},
10 | }) {
11 | return (
12 | <_Component className={_utils.cx(_styles, "section")} tag="section">
13 | <_Builtin.Container
14 | className={_utils.cx(_styles, "container", "center")}
15 | tag="div"
16 | >
17 | <_Builtin.Heading className={_utils.cx(_styles, "mb2")} tag="h2">
18 | {headingText}
19 |
20 | <_Builtin.Paragraph className={_utils.cx(_styles, "mw-70ch", "mb2")}>
21 | {
22 | "Welcome to the Visual Developers Job Board - your one-stop destination for discovering the most exciting opportunities in Webflow, no-code, and traditional development. Empower your career by connecting with innovative companies that value creativity and technical expertise in equal measure."
23 | }
24 |
25 | <_Builtin.Link
26 | className={_utils.cx(_styles, "button")}
27 | button={true}
28 | options={{
29 | href: "#",
30 | }}
31 | {...button}
32 | >
33 | {"Get started"}
34 |
35 |
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/devlink/Cta.module.css:
--------------------------------------------------------------------------------
1 | .section {
2 | padding-top: 5em;
3 | padding-bottom: 5em;
4 | }
5 |
6 | .section.hero {
7 | position: relative;
8 | min-height: 600px;
9 | background-color: var(--clrPrimary600-aeaf7110);
10 | color: var(--clrNeutral100-f9191126);
11 | }
12 |
13 | .section.title {
14 | background-color: var(--clrPrimary400-6c00fdf1);
15 | color: var(--clrPrimary999-b0a60ce1);
16 | }
17 |
18 | .section.pricing-gradient {
19 | background-image: radial-gradient(circle farthest-corner at 50% 100%, var(--clrPrimary400-6c00fdf1), var(--clrPrimary999-b0a60ce1) 73%);
20 | color: var(--clrNeutral100-f9191126);
21 | }
22 |
23 | .section.pricing {
24 | position: relative;
25 | padding-top: 5em;
26 | color: hsla(329.9999999999999, 0.00%, 100.00%, 1.00);
27 | text-align: center;
28 | }
29 |
30 | .clr-secondary-400 {
31 | background-color: var(--clrSecondary400-685c3959);
32 | }
33 |
34 | .clr-primary-400 {
35 | background-color: var(--clrPrimary400-6c00fdf1);
36 | }
37 |
38 | .section.features {
39 | color: var(--clrPrimary600-aeaf7110);
40 | }
41 |
42 | .container {
43 | width: 100%;
44 | max-width: 1200px;
45 | margin-right: auto;
46 | margin-left: auto;
47 | padding-right: 1em;
48 | padding-left: 1em;
49 | }
50 |
51 | @media screen and (max-width: 767px) {
52 | .container {
53 | padding-right: 1.5em;
54 | padding-left: 1.5em;
55 | }
56 | }
57 |
58 | .container.nav {
59 | display: flex;
60 | justify-content: space-between;
61 | align-items: center;
62 | }
63 |
64 | .container.narrow {
65 | max-width: 720px;
66 | }
67 |
68 | .container.rel {
69 | position: relative;
70 | z-index: 2;
71 | }
72 |
73 | .center {
74 | text-align: center;
75 | }
76 |
77 | .mb2 {
78 | margin-bottom: 2rem;
79 | }
80 |
81 | .mw-70ch {
82 | max-width: 70ch;
83 | margin-right: auto;
84 | margin-left: auto;
85 | }
86 |
87 | .button {
88 | padding: 1em 2em;
89 |
90 | border-radius: 2em;
91 |
92 | background-color: hsla(247.6190476190476, 42.28%, 29.22%, 1.00);
93 |
94 | box-shadow: none;
95 |
96 | transition: background-color 200ms ease,color 200ms ease;
97 |
98 | color: hsla(247.05882352941174, 0.00%, 100.00%, 1.00);
99 | font-size: 1.3rem;
100 | font-weight: 700;
101 | }
102 |
103 | .button:hover {
104 | background-color: hsla(196.55172413793105, 54.72%, 79.22%, 1.00);
105 | box-shadow: none;
106 | color: hsla(247.6190476190476, 42.28%, 29.22%, 1.00);
107 | }
108 |
109 | .button:focus {
110 | outline-color: var(--clrPrimary600-aeaf7110);
111 | outline-offset: 0.2rem;
112 | outline-style: solid;
113 | outline-width: 0.2rem;
114 | }
115 |
116 | .button:focus-visible {
117 | outline-color: var(--clrPrimary600-aeaf7110);
118 | outline-offset: 0.2rem;
119 | outline-style: solid;
120 | outline-width: 0.2rem;
121 | }
122 |
123 | .button.secondary {
124 | background-color: var(--clrSecondary400-685c3959);
125 | color: var(--clrPrimary600-aeaf7110);
126 | }
127 |
128 | .button.secondary:hover {
129 | background-color: var(--clrPrimary400-6c00fdf1);
130 | color: var(--clrNeutral100-f9191126);
131 | }
132 |
133 | .button.secondary:focus {
134 | outline-color: var(--clrSecondary400-685c3959);
135 | }
136 |
137 | .button.tertiary {
138 | background-color: var(--clrQuarternary400-68656429);
139 | color: var(--clrPrimary600-aeaf7110);
140 | }
141 |
142 | .button.tertiary:hover {
143 | background-color: var(--clrPrimary600-aeaf7110);
144 | color: var(--clrNeutral100-f9191126);
145 | }
146 |
147 | .button.tertiary:focus {
148 | outline-color: var(--clrQuarternary400-68656429);
149 | }
150 |
151 | .button.small {
152 | padding: 1em 1.5em;
153 | font-size: 1rem;
154 | }
155 |
156 | .button.sign-up {
157 | text-align: center;
158 | }
--------------------------------------------------------------------------------
/devlink/Details.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as Types from "./types";
3 |
4 | declare function Details(props: {
5 | as?: React.ElementType;
6 | company?: React.ReactNode;
7 | location?: React.ReactNode;
8 | category?: React.ReactNode;
9 | description?: React.ReactNode;
10 | benefits?: React.ReactNode;
11 | applyText?: React.ReactNode;
12 | applyLink?: Types.Basic.Link;
13 | }): React.JSX.Element;
14 |
--------------------------------------------------------------------------------
/devlink/Details.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _utils from "./utils";
4 | import _styles from "./Details.module.css";
5 |
6 | export function Details({
7 | as: _Component = _Builtin.Section,
8 | company = "This is some text inside of a div block.",
9 | location = "This is some text inside of a div block.",
10 | category = "category",
11 | description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.",
12 | benefits = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.",
13 | applyText = "Apply now",
14 |
15 | applyLink = {
16 | href: "#",
17 | },
18 | }) {
19 | return (
20 | <_Component className={_utils.cx(_styles, "section")} tag="div">
21 | <_Builtin.Container className={_utils.cx(_styles, "container")} tag="div">
22 | <_Builtin.Block
23 | className={_utils.cx(_styles, "jobs-page-grid")}
24 | tag="div"
25 | >
26 | <_Builtin.Block
27 | className={_utils.cx(_styles, "job-meta-wrapper")}
28 | id={_utils.cx(
29 | _styles,
30 | "w-node-b5a5ddfd-6cd1-01fb-2140-91ef72d47996-72d47991"
31 | )}
32 | tag="div"
33 | >
34 | <_Builtin.Block
35 | className={_utils.cx(_styles, "job-meta-item")}
36 | tag="div"
37 | >
38 | <_Builtin.Block
39 | className={_utils.cx(_styles, "job-label")}
40 | tag="div"
41 | >
42 | {"Company"}
43 |
44 | <_Builtin.Block tag="div">{company}
45 |
46 | <_Builtin.Block
47 | className={_utils.cx(_styles, "job-meta-item")}
48 | tag="div"
49 | >
50 | <_Builtin.Block
51 | className={_utils.cx(_styles, "job-label")}
52 | tag="div"
53 | >
54 | {"Location"}
55 |
56 | <_Builtin.Block tag="div">{location}
57 |
58 | <_Builtin.Block
59 | className={_utils.cx(_styles, "job-meta-item")}
60 | tag="div"
61 | >
62 | <_Builtin.Block
63 | className={_utils.cx(_styles, "category-chip")}
64 | tag="div"
65 | >
66 | {category}
67 |
68 |
69 |
70 | <_Builtin.Block
71 | id={_utils.cx(
72 | _styles,
73 | "w-node-b5a5ddfd-6cd1-01fb-2140-91ef72d479a4-72d47991"
74 | )}
75 | tag="div"
76 | >
77 | <_Builtin.Heading className={_utils.cx(_styles, "mt0")} tag="h3">
78 | {"About the role"}
79 |
80 | <_Builtin.Paragraph>{description}
81 | <_Builtin.Heading tag="h3">{"Benefits"}
82 | <_Builtin.Paragraph className={_utils.cx(_styles, "mb2")}>
83 | {benefits}
84 |
85 | <_Builtin.Link
86 | className={_utils.cx(_styles, "button")}
87 | button={true}
88 | options={applyLink}
89 | >
90 | {applyText}
91 |
92 |
93 |
94 |
95 |
96 | );
97 | }
98 |
--------------------------------------------------------------------------------
/devlink/Details.module.css:
--------------------------------------------------------------------------------
1 | .section {
2 | padding-top: 5em;
3 | padding-bottom: 5em;
4 | }
5 |
6 | .section.hero {
7 | position: relative;
8 | min-height: 600px;
9 | background-color: var(--clrPrimary600-aeaf7110);
10 | color: var(--clrNeutral100-f9191126);
11 | }
12 |
13 | .section.title {
14 | background-color: var(--clrPrimary400-6c00fdf1);
15 | color: var(--clrPrimary999-b0a60ce1);
16 | }
17 |
18 | .section.pricing-gradient {
19 | background-image: radial-gradient(circle farthest-corner at 50% 100%, var(--clrPrimary400-6c00fdf1), var(--clrPrimary999-b0a60ce1) 73%);
20 | color: var(--clrNeutral100-f9191126);
21 | }
22 |
23 | .section.pricing {
24 | position: relative;
25 | padding-top: 5em;
26 | color: hsla(329.9999999999999, 0.00%, 100.00%, 1.00);
27 | text-align: center;
28 | }
29 |
30 | .clr-secondary-400 {
31 | background-color: var(--clrSecondary400-685c3959);
32 | }
33 |
34 | .clr-primary-400 {
35 | background-color: var(--clrPrimary400-6c00fdf1);
36 | }
37 |
38 | .section.features {
39 | color: var(--clrPrimary600-aeaf7110);
40 | }
41 |
42 | .container {
43 | width: 100%;
44 | max-width: 1200px;
45 | margin-right: auto;
46 | margin-left: auto;
47 | padding-right: 1em;
48 | padding-left: 1em;
49 | }
50 |
51 | @media screen and (max-width: 767px) {
52 | .container {
53 | padding-right: 1.5em;
54 | padding-left: 1.5em;
55 | }
56 | }
57 |
58 | .container.nav {
59 | display: flex;
60 | justify-content: space-between;
61 | align-items: center;
62 | }
63 |
64 | .container.narrow {
65 | max-width: 720px;
66 | }
67 |
68 | .container.rel {
69 | position: relative;
70 | z-index: 2;
71 | }
72 |
73 | .center {
74 | text-align: center;
75 | }
76 |
77 | .jobs-page-grid {
78 | display: grid;
79 | grid-auto-columns: 1fr;
80 | grid-column-gap: 16px;
81 | grid-row-gap: 16px;
82 | grid-template-columns: 0.5fr 1fr;
83 | grid-template-rows: auto;
84 | }
85 |
86 | @media screen and (max-width: 767px) {
87 | .jobs-page-grid {
88 | grid-template-columns: 1fr;
89 | }
90 | }
91 |
92 | .job-meta-wrapper {
93 | display: flex;
94 | flex-direction: column;
95 | grid-row-gap: 1em;
96 | }
97 |
98 | .job-meta-item {
99 | display: flex;
100 | flex-direction: column;
101 | align-items: flex-start;
102 | }
103 |
104 | .job-label {
105 | font-weight: 700;
106 | }
107 |
108 | .category-chip {
109 | display: block;
110 | padding: 0.1em 1em;
111 | border-radius: 2em;
112 | background-color: var(--clrSecondary400-685c3959);
113 | color: var(--clrPrimary600-aeaf7110);
114 | }
115 |
116 | .mt0 {
117 | margin-top: 0px;
118 | }
119 |
120 | .mb2 {
121 | margin-bottom: 2rem;
122 | }
123 |
124 | .button {
125 | padding: 1em 2em;
126 |
127 | border-radius: 2em;
128 |
129 | background-color: hsla(247.6190476190476, 42.28%, 29.22%, 1.00);
130 |
131 | box-shadow: none;
132 |
133 | transition: background-color 200ms ease,color 200ms ease;
134 |
135 | color: hsla(247.05882352941174, 0.00%, 100.00%, 1.00);
136 | font-size: 1.3rem;
137 | font-weight: 700;
138 | }
139 |
140 | .button:hover {
141 | background-color: hsla(196.55172413793105, 54.72%, 79.22%, 1.00);
142 | box-shadow: none;
143 | color: hsla(247.6190476190476, 42.28%, 29.22%, 1.00);
144 | }
145 |
146 | .button:focus {
147 | outline-color: var(--clrPrimary600-aeaf7110);
148 | outline-offset: 0.2rem;
149 | outline-style: solid;
150 | outline-width: 0.2rem;
151 | }
152 |
153 | .button:focus-visible {
154 | outline-color: var(--clrPrimary600-aeaf7110);
155 | outline-offset: 0.2rem;
156 | outline-style: solid;
157 | outline-width: 0.2rem;
158 | }
159 |
160 | .button.secondary {
161 | background-color: var(--clrSecondary400-685c3959);
162 | color: var(--clrPrimary600-aeaf7110);
163 | }
164 |
165 | .button.secondary:hover {
166 | background-color: var(--clrPrimary400-6c00fdf1);
167 | color: var(--clrNeutral100-f9191126);
168 | }
169 |
170 | .button.secondary:focus {
171 | outline-color: var(--clrSecondary400-685c3959);
172 | }
173 |
174 | .button.tertiary {
175 | background-color: var(--clrQuarternary400-68656429);
176 | color: var(--clrPrimary600-aeaf7110);
177 | }
178 |
179 | .button.tertiary:hover {
180 | background-color: var(--clrPrimary600-aeaf7110);
181 | color: var(--clrNeutral100-f9191126);
182 | }
183 |
184 | .button.tertiary:focus {
185 | outline-color: var(--clrQuarternary400-68656429);
186 | }
187 |
188 | .button.small {
189 | padding: 1em 1.5em;
190 | font-size: 1rem;
191 | }
192 |
193 | .button.sign-up {
194 | text-align: center;
195 | }
196 |
197 | #w-node-b5a5ddfd-6cd1-01fb-2140-91ef72d47996-72d47991 {
198 | grid-column-start: span 1;
199 | grid-column-end: span 1;
200 | grid-row-start: span 1;
201 | grid-row-end: span 1;
202 | }
203 | #w-node-b5a5ddfd-6cd1-01fb-2140-91ef72d479a4-72d47991 {
204 | grid-column-start: span 1;
205 | grid-column-end: span 1;
206 | grid-row-start: span 1;
207 | grid-row-end: span 1;
208 | }
--------------------------------------------------------------------------------
/devlink/Features.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 |
3 | declare function Features(props: { as?: React.ElementType }): React.JSX.Element;
4 |
--------------------------------------------------------------------------------
/devlink/Features.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _utils from "./utils";
4 | import _styles from "./Features.module.css";
5 |
6 | export function Features({ as: _Component = _Builtin.Section }) {
7 | return (
8 | <_Component
9 | className={_utils.cx(_styles, "section", "features")}
10 | tag="section"
11 | >
12 | <_Builtin.Container className={_utils.cx(_styles, "container")} tag="div">
13 | <_Builtin.Block
14 | className={_utils.cx(_styles, "center", "mb2")}
15 | tag="div"
16 | >
17 | <_Builtin.Heading tag="h2">{"Features"}
18 | <_Builtin.Paragraph>
19 | {
20 | "Unlock the Power of the Visual Developers Job Board - Your Ultimate Resource for Development Opportunities"
21 | }
22 |
23 |
24 | <_Builtin.Block
25 | className={_utils.cx(_styles, "three-columns")}
26 | tag="div"
27 | >
28 | <_Builtin.Block
29 | className={_utils.cx(_styles, "feature-card")}
30 | id={_utils.cx(
31 | _styles,
32 | "w-node-_95e9dcd2-35a4-35c7-cf9c-c5baf1e2c917-f1e2c90f"
33 | )}
34 | tag="div"
35 | >
36 | <_Builtin.Block
37 | className={_utils.cx(_styles, "feature-title-wrapper")}
38 | tag="div"
39 | >
40 | <_Builtin.Image
41 | className={_utils.cx(_styles, "feature-icon")}
42 | loading="lazy"
43 | width="auto"
44 | height="auto"
45 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/645407fd9fc7ae328679723e_curated.png"
46 | />
47 | <_Builtin.Block tag="div">
48 | {"Curated Job Listings"}
49 |
50 |
51 | <_Builtin.Paragraph className={_utils.cx(_styles, "p-small")}>
52 | {
53 | "Access a diverse range of high-quality job opportunities in Webflow, no-code, and traditional development, hand-picked by our expert team."
54 | }
55 |
56 |
57 | <_Builtin.Block
58 | className={_utils.cx(_styles, "feature-card")}
59 | id={_utils.cx(
60 | _styles,
61 | "w-node-_95e9dcd2-35a4-35c7-cf9c-c5baf1e2c91e-f1e2c90f"
62 | )}
63 | tag="div"
64 | >
65 | <_Builtin.Block
66 | className={_utils.cx(_styles, "feature-title-wrapper")}
67 | tag="div"
68 | >
69 | <_Builtin.Image
70 | className={_utils.cx(_styles, "feature-icon")}
71 | loading="lazy"
72 | width="auto"
73 | height="auto"
74 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/645407fe16b9ae509ed7c888_personalized.png"
75 | />
76 | <_Builtin.Block tag="div">
77 | {"Personalized Recommendations"}
78 |
79 |
80 | <_Builtin.Paragraph className={_utils.cx(_styles, "p-small")}>
81 | {
82 | "Enjoy a tailored job search experience with customized recommendations based on your unique skills, preferences, and career goals."
83 | }
84 |
85 |
86 | <_Builtin.Block
87 | className={_utils.cx(_styles, "feature-card")}
88 | id={_utils.cx(
89 | _styles,
90 | "w-node-_95e9dcd2-35a4-35c7-cf9c-c5baf1e2c925-f1e2c90f"
91 | )}
92 | tag="div"
93 | >
94 | <_Builtin.Block
95 | className={_utils.cx(_styles, "feature-title-wrapper")}
96 | tag="div"
97 | >
98 | <_Builtin.Image
99 | className={_utils.cx(_styles, "feature-icon")}
100 | loading="lazy"
101 | width="auto"
102 | height="auto"
103 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/645407fe73cfb1455881c4f3_exclusive.png"
104 | />
105 | <_Builtin.Block tag="div">
106 | {"Exclusive Opportunities"}
107 |
108 |
109 | <_Builtin.Paragraph className={_utils.cx(_styles, "p-small")}>
110 | {
111 | "Stay ahead of the competition with early access to job listings, networking events, and industry insights available only to our community members."
112 | }
113 |
114 |
115 | <_Builtin.Block
116 | className={_utils.cx(_styles, "feature-card")}
117 | id={_utils.cx(
118 | _styles,
119 | "w-node-_95e9dcd2-35a4-35c7-cf9c-c5baf1e2c92c-f1e2c90f"
120 | )}
121 | tag="div"
122 | >
123 | <_Builtin.Block
124 | className={_utils.cx(_styles, "feature-title-wrapper")}
125 | tag="div"
126 | >
127 | <_Builtin.Image
128 | className={_utils.cx(_styles, "feature-icon")}
129 | loading="lazy"
130 | width="auto"
131 | height="auto"
132 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/645407feadf27949cb5f8faf_Frame%202.png"
133 | />
134 | <_Builtin.Block tag="div">
135 | {"Comprehensive Resources"}
136 |
137 |
138 | <_Builtin.Paragraph className={_utils.cx(_styles, "p-small")}>
139 | {
140 | "Boost your skills and knowledge with our extensive library of articles, tutorials, webinars, and more, designed to help you excel in your career."
141 | }
142 |
143 |
144 | <_Builtin.Block
145 | className={_utils.cx(_styles, "feature-card")}
146 | id={_utils.cx(
147 | _styles,
148 | "w-node-_95e9dcd2-35a4-35c7-cf9c-c5baf1e2c933-f1e2c90f"
149 | )}
150 | tag="div"
151 | >
152 | <_Builtin.Block
153 | className={_utils.cx(_styles, "feature-title-wrapper")}
154 | tag="div"
155 | >
156 | <_Builtin.Image
157 | className={_utils.cx(_styles, "feature-icon")}
158 | loading="lazy"
159 | width="auto"
160 | height="auto"
161 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/645407fdd0924e5433d823a2_community.png"
162 | />
163 | <_Builtin.Block tag="div">{"Community Support"}
164 |
165 | <_Builtin.Paragraph className={_utils.cx(_styles, "p-small")}>
166 | {
167 | "Join a thriving network of like-minded professionals, share your experiences, and collaborate on projects to drive innovation and growth in the development space."
168 | }
169 |
170 |
171 | <_Builtin.Block
172 | className={_utils.cx(_styles, "feature-card")}
173 | id={_utils.cx(
174 | _styles,
175 | "w-node-_95e9dcd2-35a4-35c7-cf9c-c5baf1e2c93a-f1e2c90f"
176 | )}
177 | tag="div"
178 | >
179 | <_Builtin.Block
180 | className={_utils.cx(_styles, "feature-title-wrapper")}
181 | tag="div"
182 | >
183 | <_Builtin.Image
184 | className={_utils.cx(_styles, "feature-icon")}
185 | loading="lazy"
186 | width="auto"
187 | height="auto"
188 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/645407fd76c2b16bd34752a1_money.png"
189 | />
190 | <_Builtin.Block tag="div">{"Flexible Pricing"}
191 |
192 | <_Builtin.Paragraph className={_utils.cx(_styles, "p-small")}>
193 | {
194 | "Choose from a variety of plans to suit your needs, whether you're a job seeker, employer, or student, and get the most out of your Visual Developers Job Board experience."
195 | }
196 |
197 |
198 |
199 |
200 |
201 | );
202 | }
203 |
--------------------------------------------------------------------------------
/devlink/Features.module.css:
--------------------------------------------------------------------------------
1 | .section {
2 | padding-top: 5em;
3 | padding-bottom: 5em;
4 | }
5 |
6 | .section.hero {
7 | position: relative;
8 | min-height: 600px;
9 | background-color: var(--clrPrimary600-aeaf7110);
10 | color: var(--clrNeutral100-f9191126);
11 | }
12 |
13 | .section.title {
14 | background-color: var(--clrPrimary400-6c00fdf1);
15 | color: var(--clrPrimary999-b0a60ce1);
16 | }
17 |
18 | .section.pricing-gradient {
19 | background-image: radial-gradient(circle farthest-corner at 50% 100%, var(--clrPrimary400-6c00fdf1), var(--clrPrimary999-b0a60ce1) 73%);
20 | color: var(--clrNeutral100-f9191126);
21 | }
22 |
23 | .section.pricing {
24 | position: relative;
25 | padding-top: 5em;
26 | color: hsla(329.9999999999999, 0.00%, 100.00%, 1.00);
27 | text-align: center;
28 | }
29 |
30 | .clr-secondary-400 {
31 | background-color: var(--clrSecondary400-685c3959);
32 | }
33 |
34 | .clr-primary-400 {
35 | background-color: var(--clrPrimary400-6c00fdf1);
36 | }
37 |
38 | .section.features {
39 | color: var(--clrPrimary600-aeaf7110);
40 | }
41 |
42 | .container {
43 | width: 100%;
44 | max-width: 1200px;
45 | margin-right: auto;
46 | margin-left: auto;
47 | padding-right: 1em;
48 | padding-left: 1em;
49 | }
50 |
51 | @media screen and (max-width: 767px) {
52 | .container {
53 | padding-right: 1.5em;
54 | padding-left: 1.5em;
55 | }
56 | }
57 |
58 | .container.nav {
59 | display: flex;
60 | justify-content: space-between;
61 | align-items: center;
62 | }
63 |
64 | .container.narrow {
65 | max-width: 720px;
66 | }
67 |
68 | .container.rel {
69 | position: relative;
70 | z-index: 2;
71 | }
72 |
73 | .center {
74 | text-align: center;
75 | }
76 |
77 | .mb2 {
78 | margin-bottom: 2rem;
79 | }
80 |
81 | .three-columns {
82 | display: grid;
83 | grid-auto-columns: 1fr;
84 | grid-column-gap: 1em;
85 | grid-row-gap: 1em;
86 | grid-template-columns: 1fr 1fr 1fr;
87 | grid-template-rows: auto auto;
88 | }
89 |
90 | @media screen and (max-width: 991px) {
91 | .three-columns {
92 | grid-template-columns: 1fr 1fr;
93 | }
94 | }
95 |
96 | @media screen and (max-width: 767px) {
97 | .three-columns {
98 | grid-template-columns: 1fr;
99 | }
100 | }
101 |
102 | .feature-card {
103 | display: flex;
104 | padding: 2em;
105 | flex-direction: column;
106 | grid-row-gap: 1em;
107 |
108 | border-radius: 2em;
109 |
110 | background-color: hsla(210, 13.33%, 94.12%, 0.35);
111 | }
112 |
113 | .feature-title-wrapper {
114 | display: flex;
115 | align-items: center;
116 | grid-column-gap: 1em;
117 | font-weight: 700;
118 | }
119 |
120 | @media screen and (max-width: 479px) {
121 | .feature-title-wrapper {
122 | flex-direction: column;
123 | align-items: flex-start;
124 | grid-row-gap: 1em;
125 | }
126 | }
127 |
128 | .feature-icon {
129 | width: 40px;
130 | }
131 |
132 | .p-small {
133 | font-size: 1rem;
134 | }
135 |
136 | #w-node-_95e9dcd2-35a4-35c7-cf9c-c5baf1e2c917-f1e2c90f {
137 | grid-column-start: span 1;
138 | grid-column-end: span 1;
139 | grid-row-start: span 1;
140 | grid-row-end: span 1;
141 | }
142 | #w-node-_95e9dcd2-35a4-35c7-cf9c-c5baf1e2c91e-f1e2c90f {
143 | grid-column-start: span 1;
144 | grid-column-end: span 1;
145 | grid-row-start: span 1;
146 | grid-row-end: span 1;
147 | }
148 | #w-node-_95e9dcd2-35a4-35c7-cf9c-c5baf1e2c925-f1e2c90f {
149 | grid-column-start: span 1;
150 | grid-column-end: span 1;
151 | grid-row-start: span 1;
152 | grid-row-end: span 1;
153 | }
154 | #w-node-_95e9dcd2-35a4-35c7-cf9c-c5baf1e2c92c-f1e2c90f {
155 | grid-column-start: span 1;
156 | grid-column-end: span 1;
157 | grid-row-start: span 1;
158 | grid-row-end: span 1;
159 | }
160 | #w-node-_95e9dcd2-35a4-35c7-cf9c-c5baf1e2c933-f1e2c90f {
161 | grid-column-start: span 1;
162 | grid-column-end: span 1;
163 | grid-row-start: span 1;
164 | grid-row-end: span 1;
165 | }
166 | #w-node-_95e9dcd2-35a4-35c7-cf9c-c5baf1e2c93a-f1e2c90f {
167 | grid-column-start: span 1;
168 | grid-column-end: span 1;
169 | grid-row-start: span 1;
170 | grid-row-end: span 1;
171 | }
--------------------------------------------------------------------------------
/devlink/Footer.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as Types from "./types";
3 |
4 | declare function Footer(props: {
5 | as?: React.ElementType;
6 | year?: React.ReactNode;
7 | }): React.JSX.Element;
8 |
--------------------------------------------------------------------------------
/devlink/Footer.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _utils from "./utils";
4 | import _styles from "./Footer.module.css";
5 |
6 | export function Footer({ as: _Component = _Builtin.Section, year = "XXXX" }) {
7 | return (
8 | <_Component className={_utils.cx(_styles, "footer")} tag="footer">
9 | <_Builtin.Container className={_utils.cx(_styles, "container")} tag="div">
10 | <_Builtin.List
11 | className={_utils.cx(_styles, "footer-list")}
12 | tag="ul"
13 | unstyled={false}
14 | >
15 | <_Builtin.ListItem className={_utils.cx(_styles, "navlist-item")}>
16 | <_Builtin.Link
17 | className={_utils.cx(_styles, "footer-link")}
18 | button={false}
19 | options={{
20 | href: "/",
21 | }}
22 | >
23 | {"Home"}
24 |
25 |
26 | <_Builtin.ListItem className={_utils.cx(_styles, "navlist-item")}>
27 | <_Builtin.Link
28 | className={_utils.cx(_styles, "footer-link")}
29 | button={false}
30 | options={{
31 | href: "/about",
32 | }}
33 | >
34 | {"About"}
35 |
36 |
37 | <_Builtin.ListItem className={_utils.cx(_styles, "navlist-item")}>
38 | <_Builtin.Link
39 | className={_utils.cx(_styles, "footer-link")}
40 | button={false}
41 | options={{
42 | href: "/jobs",
43 | }}
44 | >
45 | {"Jobs"}
46 |
47 |
48 | <_Builtin.ListItem className={_utils.cx(_styles, "navlist-item")}>
49 | <_Builtin.Link
50 | className={_utils.cx(_styles, "footer-link")}
51 | button={false}
52 | options={{
53 | href: "https://webflow.com",
54 | target: "_blank",
55 | }}
56 | >
57 | {"Webflow"}
58 |
59 |
60 | <_Builtin.ListItem className={_utils.cx(_styles, "navlist-item")}>
61 | <_Builtin.Link
62 | className={_utils.cx(_styles, "footer-link")}
63 | button={false}
64 | options={{
65 | href: "https://webflow.com/devlink",
66 | target: "_blank",
67 | }}
68 | >
69 | {"DevLink"}
70 |
71 |
72 |
73 | <_Builtin.Block
74 | className={_utils.cx(_styles, "footer-small")}
75 | tag="div"
76 | >
77 | <_Builtin.Block tag="div">{"©"}
78 | <_Builtin.Block tag="div">{year}
79 | <_Builtin.Block tag="div">
80 | {
81 | "Visual Development Job Board. This is all fake. Content by ChatGPT , built in Webflow using DevLink"
82 | }
83 |
84 |
85 |
86 |
87 | );
88 | }
89 |
--------------------------------------------------------------------------------
/devlink/Footer.module.css:
--------------------------------------------------------------------------------
1 | .footer {
2 | padding-top: 5em;
3 | padding-bottom: 5em;
4 | background-color: var(--clrPrimary600-aeaf7110);
5 | color: var(--clrNeutral100-f9191126);
6 | }
7 |
8 | .container {
9 | width: 100%;
10 | max-width: 1200px;
11 | margin-right: auto;
12 | margin-left: auto;
13 | padding-right: 1em;
14 | padding-left: 1em;
15 | }
16 |
17 | @media screen and (max-width: 767px) {
18 | .container {
19 | padding-right: 1.5em;
20 | padding-left: 1.5em;
21 | }
22 | }
23 |
24 | .container.nav {
25 | display: flex;
26 | justify-content: space-between;
27 | align-items: center;
28 | }
29 |
30 | .container.narrow {
31 | max-width: 720px;
32 | }
33 |
34 | .container.rel {
35 | position: relative;
36 | z-index: 2;
37 | }
38 |
39 | .center {
40 | text-align: center;
41 | }
42 |
43 | .footer-list {
44 | display: flex;
45 | margin-bottom: 2em;
46 | padding-left: 0px;
47 | justify-content: center;
48 | align-items: center;
49 | grid-column-gap: 1em;
50 |
51 | list-style-type: none;
52 | }
53 |
54 | .navlist-item {
55 | margin-bottom: 0em;
56 | }
57 |
58 | .footer-link {
59 | color: var(--clrNeutral100-f9191126);
60 | }
61 |
62 | .footer-link:focus {
63 | border-radius: 0em;
64 | outline-color: var(--clrNeutral100-f9191126);
65 | outline-offset: 0.3rem;
66 | outline-style: solid;
67 | outline-width: 0.2rem;
68 | }
69 |
70 | .footer-link:focus-visible {
71 | outline-color: var(--clrNeutral100-f9191126);
72 | outline-offset: 0.3rem;
73 | outline-style: solid;
74 | outline-width: 0.2rem;
75 | }
76 |
77 | .footer-small {
78 | display: flex;
79 | justify-content: center;
80 | align-items: center;
81 | grid-column-gap: 0.25em;
82 |
83 | font-size: 0.8rem;
84 | text-align: center;
85 | }
--------------------------------------------------------------------------------
/devlink/Hero.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as Types from "./types";
3 |
4 | declare function Hero(props: {
5 | as?: React.ElementType;
6 | buttonText?: React.ReactNode;
7 | buttonLink?: Types.Basic.Link;
8 | }): React.JSX.Element;
9 |
--------------------------------------------------------------------------------
/devlink/Hero.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _utils from "./utils";
4 | import _styles from "./Hero.module.css";
5 |
6 | export function Hero({
7 | as: _Component = _Builtin.Section,
8 | buttonText = "View open roles",
9 |
10 | buttonLink = {
11 | href: "#",
12 | },
13 | }) {
14 | return (
15 | <_Component className={_utils.cx(_styles, "section", "hero")} tag="section">
16 | <_Builtin.Container className={_utils.cx(_styles, "container")} tag="div">
17 | <_Builtin.Block className={_utils.cx(_styles, "two-columns")} tag="div">
18 | <_Builtin.Block
19 | className={_utils.cx(_styles, "hero-content-wrapper")}
20 | id={_utils.cx(
21 | _styles,
22 | "w-node-eec4ca81-6ae6-a937-d67c-2a2debaf4e5f-ebaf4e5c"
23 | )}
24 | tag="div"
25 | >
26 | <_Builtin.Heading
27 | className={_utils.cx(_styles, "hero-heading")}
28 | tag="h1"
29 | >
30 | {"Visual"}
31 |
32 | {"Development"}
33 |
34 | {"Job Board"}
35 |
36 | <_Builtin.Paragraph
37 | className={_utils.cx(_styles, "hero-paragraph")}
38 | >
39 | {
40 | "Welcome to the Visual Developers Job Board - your one-stop destination for discovering the most exciting opportunities in Webflow, no-code, and traditional development. Empower your career by connecting with innovative companies that value creativity and technical expertise in equal measure."
41 | }
42 |
43 | <_Builtin.Link
44 | className={_utils.cx(_styles, "button", "secondary")}
45 | button={true}
46 | options={buttonLink}
47 | >
48 | {buttonText}
49 |
50 |
51 | <_Builtin.Block
52 | className={_utils.cx(_styles, "hero-image-wrapper")}
53 | id={_utils.cx(
54 | _styles,
55 | "w-node-eec4ca81-6ae6-a937-d67c-2a2debaf4e6a-ebaf4e5c"
56 | )}
57 | tag="div"
58 | >
59 | <_Builtin.Image
60 | className={_utils.cx(_styles, "hero-image")}
61 | loading="eager"
62 | width="auto"
63 | height="auto"
64 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/644ea54b57472ff521fca399_main-object.png"
65 | />
66 |
67 |
68 |
69 |
70 | );
71 | }
72 |
--------------------------------------------------------------------------------
/devlink/Hero.module.css:
--------------------------------------------------------------------------------
1 | .section {
2 | padding-top: 5em;
3 | padding-bottom: 5em;
4 | }
5 |
6 | .section.hero {
7 | position: relative;
8 | min-height: 600px;
9 | background-color: var(--clrPrimary600-aeaf7110);
10 | color: var(--clrNeutral100-f9191126);
11 | }
12 |
13 | .section.title {
14 | background-color: var(--clrPrimary400-6c00fdf1);
15 | color: var(--clrPrimary999-b0a60ce1);
16 | }
17 |
18 | .section.pricing-gradient {
19 | background-image: radial-gradient(circle farthest-corner at 50% 100%, var(--clrPrimary400-6c00fdf1), var(--clrPrimary999-b0a60ce1) 73%);
20 | color: var(--clrNeutral100-f9191126);
21 | }
22 |
23 | .section.pricing {
24 | position: relative;
25 | padding-top: 5em;
26 | color: hsla(329.9999999999999, 0.00%, 100.00%, 1.00);
27 | text-align: center;
28 | }
29 |
30 | .clr-secondary-400 {
31 | background-color: var(--clrSecondary400-685c3959);
32 | }
33 |
34 | .clr-primary-400 {
35 | background-color: var(--clrPrimary400-6c00fdf1);
36 | }
37 |
38 | .section.features {
39 | color: var(--clrPrimary600-aeaf7110);
40 | }
41 |
42 | .container {
43 | width: 100%;
44 | max-width: 1200px;
45 | margin-right: auto;
46 | margin-left: auto;
47 | padding-right: 1em;
48 | padding-left: 1em;
49 | }
50 |
51 | @media screen and (max-width: 767px) {
52 | .container {
53 | padding-right: 1.5em;
54 | padding-left: 1.5em;
55 | }
56 | }
57 |
58 | .container.nav {
59 | display: flex;
60 | justify-content: space-between;
61 | align-items: center;
62 | }
63 |
64 | .container.narrow {
65 | max-width: 720px;
66 | }
67 |
68 | .container.rel {
69 | position: relative;
70 | z-index: 2;
71 | }
72 |
73 | .center {
74 | text-align: center;
75 | }
76 |
77 | .two-columns {
78 | display: grid;
79 | padding-left: 0px;
80 | grid-auto-columns: 1fr;
81 | grid-column-gap: 1.5rem;
82 | grid-row-gap: 1.5rem;
83 | grid-template-columns: 1fr 1fr;
84 | grid-template-rows: auto;
85 | }
86 |
87 | @media screen and (max-width: 767px) {
88 | .two-columns {
89 | grid-template-columns: 1fr;
90 | }
91 | }
92 |
93 | .two-columns.narrow {
94 | max-width: 960px;
95 | margin: 3em auto;
96 | }
97 |
98 | .hero-content-wrapper {
99 | display: flex;
100 | flex-direction: column;
101 | flex-wrap: nowrap;
102 | align-items: flex-start;
103 | grid-row-gap: 1.2em;
104 | }
105 |
106 | .hero-heading {
107 | margin-top: 0px;
108 | margin-bottom: 0px;
109 |
110 | font-family: 'Playfair Display';
111 | font-size: 5rem;
112 | line-height: 1;
113 | font-weight: 700;
114 | text-align: left;
115 | -webkit-text-stroke-color: var(--clrPrimary600-aeaf7110);
116 | }
117 |
118 | @media screen and (max-width: 991px) {
119 | .hero-heading {
120 | font-size: 5rem;
121 | }
122 | }
123 |
124 | @media screen and (max-width: 479px) {
125 | .hero-heading {
126 | line-height: 1;
127 | }
128 | }
129 |
130 | @media screen and (max-width: 767px) {
131 | .hero-heading {
132 | font-size: 14vw;
133 | }
134 | }
135 |
136 | .hero-paragraph {
137 | max-width: 60ch;
138 | margin-bottom: 0.5em;
139 | font-size: 1.2rem;
140 | }
141 |
142 | .button {
143 | padding: 1em 2em;
144 |
145 | border-radius: 2em;
146 |
147 | background-color: hsla(247.6190476190476, 42.28%, 29.22%, 1.00);
148 |
149 | box-shadow: none;
150 |
151 | transition: background-color 200ms ease,color 200ms ease;
152 |
153 | color: hsla(247.05882352941174, 0.00%, 100.00%, 1.00);
154 | font-size: 1.3rem;
155 | font-weight: 700;
156 | }
157 |
158 | .button:hover {
159 | background-color: hsla(196.55172413793105, 54.72%, 79.22%, 1.00);
160 | box-shadow: none;
161 | color: hsla(247.6190476190476, 42.28%, 29.22%, 1.00);
162 | }
163 |
164 | .button:focus {
165 | outline-color: var(--clrPrimary600-aeaf7110);
166 | outline-offset: 0.2rem;
167 | outline-style: solid;
168 | outline-width: 0.2rem;
169 | }
170 |
171 | .button:focus-visible {
172 | outline-color: var(--clrPrimary600-aeaf7110);
173 | outline-offset: 0.2rem;
174 | outline-style: solid;
175 | outline-width: 0.2rem;
176 | }
177 |
178 | .button.secondary {
179 | background-color: var(--clrSecondary400-685c3959);
180 | color: var(--clrPrimary600-aeaf7110);
181 | }
182 |
183 | .button.secondary:hover {
184 | background-color: var(--clrPrimary400-6c00fdf1);
185 | color: var(--clrNeutral100-f9191126);
186 | }
187 |
188 | .button.secondary:focus {
189 | outline-color: var(--clrSecondary400-685c3959);
190 | }
191 |
192 | .button.tertiary {
193 | background-color: var(--clrQuarternary400-68656429);
194 | color: var(--clrPrimary600-aeaf7110);
195 | }
196 |
197 | .button.tertiary:hover {
198 | background-color: var(--clrPrimary600-aeaf7110);
199 | color: var(--clrNeutral100-f9191126);
200 | }
201 |
202 | .button.tertiary:focus {
203 | outline-color: var(--clrQuarternary400-68656429);
204 | }
205 |
206 | .button.small {
207 | padding: 1em 1.5em;
208 | font-size: 1rem;
209 | }
210 |
211 | .button.sign-up {
212 | text-align: center;
213 | }
214 |
215 | .hero-image {
216 | width: 100%;
217 | height: 100%;
218 | object-fit: contain;
219 | }
220 |
221 | #w-node-eec4ca81-6ae6-a937-d67c-2a2debaf4e5f-ebaf4e5c {
222 | grid-column-start: span 1;
223 | grid-column-end: span 1;
224 | grid-row-start: span 1;
225 | grid-row-end: span 1;
226 | align-self: center;
227 | }
228 | #w-node-eec4ca81-6ae6-a937-d67c-2a2debaf4e6a-ebaf4e5c {
229 | grid-column-start: span 1;
230 | grid-column-end: span 1;
231 | grid-row-start: span 1;
232 | grid-row-end: span 1;
233 | }
--------------------------------------------------------------------------------
/devlink/JobListing.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as Types from "./types";
3 |
4 | declare function JobListing(props: {
5 | as?: React.ElementType;
6 | companyName?: React.ReactNode;
7 | listingName?: React.ReactNode;
8 | location?: React.ReactNode;
9 | applyText?: React.ReactNode;
10 | applyLink?: Types.Basic.Link;
11 | learnText?: React.ReactNode;
12 | learnLink?: Types.Basic.Link;
13 | }): React.JSX.Element;
14 |
--------------------------------------------------------------------------------
/devlink/JobListing.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _utils from "./utils";
4 | import _styles from "./JobListing.module.css";
5 |
6 | export function JobListing({
7 | as: _Component = _Builtin.Block,
8 | companyName = "Company name",
9 | listingName = "This is some text inside of a div block.",
10 | location = "Location",
11 | applyText = "Apply now",
12 |
13 | applyLink = {
14 | href: "#",
15 | },
16 |
17 | learnText = "Learn more",
18 |
19 | learnLink = {
20 | href: "#",
21 | },
22 | }) {
23 | return (
24 | <_Component className={_utils.cx(_styles, "job-listing")} tag="div">
25 | <_Builtin.Block tag="div">
26 | <_Builtin.Block tag="div">{companyName}
27 | <_Builtin.Block
28 | className={_utils.cx(_styles, "listing-name")}
29 | tag="div"
30 | >
31 | {listingName}
32 |
33 | <_Builtin.Block tag="div">{location}
34 |
35 | <_Builtin.Block
36 | className={_utils.cx(_styles, "buttons-wrapper")}
37 | id={_utils.cx(
38 | _styles,
39 | "w-node-_7f42fc01-bdea-9976-acc3-ac76a91ba330-a91ba328"
40 | )}
41 | tag="div"
42 | >
43 | <_Builtin.Link
44 | className={_utils.cx(_styles, "button", "small")}
45 | id={_utils.cx(
46 | _styles,
47 | "w-node-_7f42fc01-bdea-9976-acc3-ac76a91ba331-a91ba328"
48 | )}
49 | button={true}
50 | options={learnLink}
51 | >
52 | {learnText}
53 |
54 | <_Builtin.Link
55 | className={_utils.cx(_styles, "button", "small", "tertiary")}
56 | button={true}
57 | options={applyLink}
58 | >
59 | {applyText}
60 |
61 |
62 |
63 | );
64 | }
65 |
--------------------------------------------------------------------------------
/devlink/JobListing.module.css:
--------------------------------------------------------------------------------
1 | .job-listing {
2 | display: grid;
3 | margin-bottom: 2em;
4 | padding: 1.5em;
5 | flex-direction: column;
6 | grid-auto-columns: 1fr;
7 | grid-column-gap: 16px;
8 | grid-row-gap: 0.25em;
9 | grid-template-columns: 1fr 0.5fr;
10 | grid-template-rows: auto;
11 |
12 | border-radius: 1em;
13 |
14 | background-color: hsla(248.5714285714286, 74.84%, 68.82%, 0.15);
15 |
16 | color: var(--clrPrimary600-aeaf7110);
17 | }
18 |
19 | @media screen and (max-width: 991px) {
20 | .job-listing {
21 | grid-row-gap: 1em;
22 | grid-template-columns: 1fr;
23 | }
24 | }
25 |
26 | .listing-name {
27 | font-size: 1.5rem;
28 | font-weight: 700;
29 | }
30 |
31 | .buttons-wrapper {
32 | display: flex;
33 | grid-column-gap: 1em;
34 | }
35 |
36 | @media screen and (max-width: 479px) {
37 | .buttons-wrapper {
38 | flex-direction: column;
39 | grid-row-gap: 1em;
40 | }
41 | }
42 |
43 | .button {
44 | padding: 1em 2em;
45 |
46 | border-radius: 2em;
47 |
48 | background-color: hsla(247.6190476190476, 42.28%, 29.22%, 1.00);
49 |
50 | box-shadow: none;
51 |
52 | transition: background-color 200ms ease,color 200ms ease;
53 |
54 | color: hsla(247.05882352941174, 0.00%, 100.00%, 1.00);
55 | font-size: 1.3rem;
56 | font-weight: 700;
57 | }
58 |
59 | .button:hover {
60 | background-color: hsla(196.55172413793105, 54.72%, 79.22%, 1.00);
61 | box-shadow: none;
62 | color: hsla(247.6190476190476, 42.28%, 29.22%, 1.00);
63 | }
64 |
65 | .button:focus {
66 | outline-color: var(--clrPrimary600-aeaf7110);
67 | outline-offset: 0.2rem;
68 | outline-style: solid;
69 | outline-width: 0.2rem;
70 | }
71 |
72 | .button:focus-visible {
73 | outline-color: var(--clrPrimary600-aeaf7110);
74 | outline-offset: 0.2rem;
75 | outline-style: solid;
76 | outline-width: 0.2rem;
77 | }
78 |
79 | .button.secondary {
80 | background-color: var(--clrSecondary400-685c3959);
81 | color: var(--clrPrimary600-aeaf7110);
82 | }
83 |
84 | .button.secondary:hover {
85 | background-color: var(--clrPrimary400-6c00fdf1);
86 | color: var(--clrNeutral100-f9191126);
87 | }
88 |
89 | .button.secondary:focus {
90 | outline-color: var(--clrSecondary400-685c3959);
91 | }
92 |
93 | .button.tertiary {
94 | background-color: var(--clrQuarternary400-68656429);
95 | color: var(--clrPrimary600-aeaf7110);
96 | }
97 |
98 | .button.tertiary:hover {
99 | background-color: var(--clrPrimary600-aeaf7110);
100 | color: var(--clrNeutral100-f9191126);
101 | }
102 |
103 | .button.tertiary:focus {
104 | outline-color: var(--clrQuarternary400-68656429);
105 | }
106 |
107 | .button.small {
108 | padding: 1em 1.5em;
109 | font-size: 1rem;
110 | }
111 |
112 | .button.sign-up {
113 | text-align: center;
114 | }
115 |
116 | #w-node-_7f42fc01-bdea-9976-acc3-ac76a91ba330-a91ba328 {
117 | grid-column-start: span 1;
118 | grid-column-end: span 1;
119 | grid-row-start: span 1;
120 | grid-row-end: span 1;
121 | justify-self: end;
122 | align-self: center;
123 | }
124 | #w-node-_7f42fc01-bdea-9976-acc3-ac76a91ba331-a91ba328 {
125 | grid-column-start: span 1;
126 | grid-column-end: span 1;
127 | grid-row-start: span 1;
128 | grid-row-end: span 1;
129 | }
130 | @media screen and (max-width: 991px) {
131 | #w-node-_7f42fc01-bdea-9976-acc3-ac76a91ba330-a91ba328 {
132 | justify-self: start;
133 | }
134 | }
--------------------------------------------------------------------------------
/devlink/Navbar.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as Types from "./types";
3 |
4 | declare function Navbar(props: {
5 | as?: React.ElementType;
6 | homeText?: React.ReactNode;
7 | homeLink?: Types.Basic.Link;
8 | aboutText?: React.ReactNode;
9 | aboutLink?: Types.Basic.Link;
10 | jobsText?: React.ReactNode;
11 | jobsLink?: Types.Basic.Link;
12 | brandLink?: Types.Basic.Link;
13 | }): React.JSX.Element;
14 |
--------------------------------------------------------------------------------
/devlink/Navbar.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _utils from "./utils";
4 | import _styles from "./Navbar.module.css";
5 |
6 | export function Navbar({
7 | as: _Component = _Builtin.NavbarWrapper,
8 | homeText = "Home",
9 |
10 | homeLink = {
11 | href: "#",
12 | },
13 |
14 | aboutText = "About",
15 |
16 | aboutLink = {
17 | href: "#",
18 | },
19 |
20 | jobsText = "Jobs",
21 |
22 | jobsLink = {
23 | href: "#",
24 | },
25 |
26 | brandLink = {
27 | href: "#",
28 | },
29 | }) {
30 | return (
31 | <_Component
32 | className={_utils.cx(_styles, "navbar")}
33 | tag="div"
34 | config={{
35 | animation: "default",
36 | collapse: "medium",
37 | docHeight: false,
38 | duration: 400,
39 | easing: "ease",
40 | easing2: "ease",
41 | noScroll: false,
42 | }}
43 | >
44 | <_Builtin.Block
45 | className={_utils.cx(_styles, "container", "nav")}
46 | tag="div"
47 | >
48 | <_Builtin.NavbarBrand
49 | className={_utils.cx(_styles, "brand")}
50 | options={brandLink}
51 | >
52 | <_Builtin.Image
53 | className={_utils.cx(_styles, "brand-image")}
54 | loading="lazy"
55 | width="auto"
56 | height="auto"
57 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/644ea5920aef435a232ffdfc_Other%2021.png"
58 | />
59 |
60 | <_Builtin.NavbarMenu
61 | className={_utils.cx(_styles, "navmenu")}
62 | tag="nav"
63 | role="navigation"
64 | >
65 | <_Builtin.NavbarLink
66 | className={_utils.cx(_styles, "navlink")}
67 | options={homeLink}
68 | >
69 | {homeText}
70 |
71 | <_Builtin.NavbarLink
72 | className={_utils.cx(_styles, "navlink")}
73 | options={aboutLink}
74 | >
75 | {aboutText}
76 |
77 | <_Builtin.NavbarLink
78 | className={_utils.cx(_styles, "navlink")}
79 | options={jobsLink}
80 | >
81 | {jobsText}
82 |
83 | <_Builtin.NavbarLink
84 | className={_utils.cx(_styles, "navlink", "nav-button")}
85 | options={{
86 | href: "#",
87 | }}
88 | >
89 | {"Post a job"}
90 |
91 |
92 | <_Builtin.NavbarButton tag="div">
93 | <_Builtin.Icon
94 | widget={{
95 | type: "icon",
96 | icon: "nav-menu",
97 | }}
98 | />
99 |
100 |
101 |
102 | );
103 | }
104 |
--------------------------------------------------------------------------------
/devlink/Navbar.module.css:
--------------------------------------------------------------------------------
1 | .navbar {
2 | padding-top: 1em;
3 | padding-bottom: 1em;
4 | background-color: var(--clrPrimary600-aeaf7110);
5 | color: var(--clrNeutral100-f9191126);
6 | }
7 |
8 | .container {
9 | width: 100%;
10 | max-width: 1200px;
11 | margin-right: auto;
12 | margin-left: auto;
13 | padding-right: 1em;
14 | padding-left: 1em;
15 | }
16 |
17 | @media screen and (max-width: 767px) {
18 | .container {
19 | padding-right: 1.5em;
20 | padding-left: 1.5em;
21 | }
22 | }
23 |
24 | .container.nav {
25 | display: flex;
26 | justify-content: space-between;
27 | align-items: center;
28 | }
29 |
30 | .container.narrow {
31 | max-width: 720px;
32 | }
33 |
34 | .container.rel {
35 | position: relative;
36 | z-index: 2;
37 | }
38 |
39 | .center {
40 | text-align: center;
41 | }
42 |
43 | .brand {
44 | font-family: 'Playfair Display';
45 | color: var(--clrNeutral100-f9191126);
46 | font-size: 2rem;
47 | font-weight: 700;
48 | }
49 |
50 | .brand:focus {
51 | border-radius: 0.3em;
52 | outline-color: var(--clrNeutral100-f9191126);
53 | outline-offset: 0.3rem;
54 | outline-style: solid;
55 | outline-width: 0.2rem;
56 | }
57 |
58 | .brand-image {
59 | width: 120px;
60 | height: 93px;
61 | }
62 |
63 | .navmenu {
64 | display: flex;
65 | justify-content: flex-end;
66 | }
67 |
68 | @media screen and (max-width: 991px) {
69 | .navmenu {
70 | padding: 2rem;
71 | background-color: var(--clrPrimary600-aeaf7110);
72 | }
73 | }
74 |
75 | @media screen and (max-width: 767px) {
76 | .navmenu {
77 | flex-direction: column;
78 | align-items: center;
79 | }
80 | }
81 |
82 | .navlink {
83 | padding: 1em 0.5em;
84 |
85 | box-shadow: inset 0 0 0 0 var(--clrSecondary400-685c3959);
86 |
87 | transition: color 200ms ease,box-shadow 200ms ease;
88 |
89 | color: var(--clrNeutral100-f9191126);
90 | font-size: 1.2rem;
91 | font-weight: 600;
92 | }
93 |
94 | .navlink:hover {
95 | box-shadow: inset 0 -5px 0 0 var(--clrSecondary400-685c3959);
96 | color: var(--clrSecondary400-685c3959);
97 | }
98 |
99 | .navlink:focus {
100 | border-radius: 0em;
101 | outline-color: var(--clrNeutral100-f9191126);
102 | outline-offset: 0.3rem;
103 | outline-style: solid;
104 | outline-width: 0.2rem;
105 | }
106 |
107 | .navlink:focus-visible {
108 | outline-color: var(--clrNeutral100-f9191126);
109 | outline-offset: 0.3rem;
110 | outline-style: solid;
111 | outline-width: 0.2rem;
112 | }
113 |
114 | .navlink:global(.w--current) {
115 | color: var(--clrNeutral100-f9191126);
116 | }
117 |
118 | .navlink:global(.w--current):hover {
119 | color: var(--clrSecondary400-685c3959);
120 | }
121 |
122 | @media screen and (max-width: 767px) {
123 | .navlink {
124 | text-align: center;
125 | }
126 | }
127 |
128 | .navlink.nav-button {
129 | padding-right: 2em;
130 | padding-left: 2em;
131 |
132 | border-radius: 2em;
133 |
134 | background-color: var(--clrQuarternary400-68656429);
135 |
136 | box-shadow: none;
137 |
138 | transition-property: none;
139 |
140 | color: var(--clrPrimary600-aeaf7110);
141 | }
142 |
143 | .navlink.nav-button:hover {
144 | background-color: var(--clrSecondary400-685c3959);
145 | color: var(--clrPrimary600-aeaf7110);
146 | }
147 |
148 | .navlink.nav-button:focus {
149 | outline-color: var(--clrQuarternary400-68656429);
150 | }
--------------------------------------------------------------------------------
/devlink/Newnav.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as Types from "./types";
3 |
4 | declare function Newnav(props: {
5 | as?: React.ElementType;
6 | brandLink?: Types.Basic.Link;
7 | homeText?: React.ReactNode;
8 | homeLink?: Types.Basic.Link;
9 | aboutText?: React.ReactNode;
10 | aboutLink?: Types.Basic.Link;
11 | jobsText?: React.ReactNode;
12 | jobsLink?: Types.Basic.Link;
13 | postText?: React.ReactNode;
14 | postLink?: Types.Basic.Link;
15 | postJob?: Types.Devlink.RuntimeProps;
16 | }): React.JSX.Element;
17 |
--------------------------------------------------------------------------------
/devlink/Newnav.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _utils from "./utils";
4 | import _styles from "./Newnav.module.css";
5 |
6 | export function Newnav({
7 | as: _Component = _Builtin.NavbarWrapper,
8 |
9 | brandLink = {
10 | href: "#",
11 | },
12 |
13 | homeText = "Home",
14 |
15 | homeLink = {
16 | href: "#",
17 | },
18 |
19 | aboutText = "About",
20 |
21 | aboutLink = {
22 | href: "#",
23 | },
24 |
25 | jobsText = "Jobs",
26 |
27 | jobsLink = {
28 | href: "#",
29 | },
30 |
31 | postText = "Post a job",
32 |
33 | postLink = {
34 | href: "#",
35 | },
36 |
37 | postJob = {},
38 | }) {
39 | return (
40 | <_Component
41 | className={_utils.cx(_styles, "navbar")}
42 | tag="div"
43 | config={{
44 | animation: "default",
45 | collapse: "medium",
46 | docHeight: false,
47 | duration: 400,
48 | easing: "ease",
49 | easing2: "ease",
50 | noScroll: false,
51 | }}
52 | >
53 | <_Builtin.Block
54 | className={_utils.cx(_styles, "container", "nav")}
55 | tag="div"
56 | >
57 | <_Builtin.NavbarBrand
58 | className={_utils.cx(_styles, "brand")}
59 | options={brandLink}
60 | >
61 | <_Builtin.Image
62 | className={_utils.cx(_styles, "brand-image")}
63 | loading="lazy"
64 | width="auto"
65 | height="auto"
66 | src="https://uploads-ssl.webflow.com/644d71f7a83e2ea6946fafd7/644ea5920aef435a232ffdfc_Other%2021.png"
67 | />
68 |
69 | <_Builtin.NavbarMenu
70 | className={_utils.cx(_styles, "navmenu")}
71 | tag="nav"
72 | role="navigation"
73 | >
74 | <_Builtin.List
75 | className={_utils.cx(_styles, "navlist")}
76 | tag="ul"
77 | unstyled={false}
78 | >
79 | <_Builtin.ListItem className={_utils.cx(_styles, "navlist-item")}>
80 | <_Builtin.NavbarLink
81 | className={_utils.cx(_styles, "navlink")}
82 | options={homeLink}
83 | >
84 | {homeText}
85 |
86 |
87 | <_Builtin.ListItem className={_utils.cx(_styles, "navlist-item")}>
88 | <_Builtin.NavbarLink
89 | className={_utils.cx(_styles, "navlink")}
90 | options={aboutLink}
91 | >
92 | {aboutText}
93 |
94 |
95 | <_Builtin.ListItem className={_utils.cx(_styles, "navlist-item")}>
96 | <_Builtin.NavbarLink
97 | className={_utils.cx(_styles, "navlink")}
98 | options={jobsLink}
99 | >
100 | {jobsText}
101 |
102 |
103 | <_Builtin.ListItem className={_utils.cx(_styles, "navlist-item")}>
104 | <_Builtin.NavbarLink
105 | className={_utils.cx(_styles, "navlink", "nav-button")}
106 | options={postLink}
107 | {...postJob}
108 | >
109 | {postText}
110 |
111 |
112 |
113 |
114 | <_Builtin.NavbarButton
115 | className={_utils.cx(_styles, "hamburger")}
116 | tag="div"
117 | >
118 | <_Builtin.Icon
119 | widget={{
120 | type: "icon",
121 | icon: "nav-menu",
122 | }}
123 | />
124 |
125 |
126 |
127 | );
128 | }
129 |
--------------------------------------------------------------------------------
/devlink/Newnav.module.css:
--------------------------------------------------------------------------------
1 | .navbar {
2 | padding-top: 1em;
3 | padding-bottom: 1em;
4 | background-color: var(--clrPrimary600-aeaf7110);
5 | color: var(--clrNeutral100-f9191126);
6 | }
7 |
8 | .container {
9 | width: 100%;
10 | max-width: 1200px;
11 | margin-right: auto;
12 | margin-left: auto;
13 | padding-right: 1em;
14 | padding-left: 1em;
15 | }
16 |
17 | @media screen and (max-width: 767px) {
18 | .container {
19 | padding-right: 1.5em;
20 | padding-left: 1.5em;
21 | }
22 | }
23 |
24 | .container.nav {
25 | display: flex;
26 | justify-content: space-between;
27 | align-items: center;
28 | }
29 |
30 | .container.narrow {
31 | max-width: 720px;
32 | }
33 |
34 | .container.rel {
35 | position: relative;
36 | z-index: 2;
37 | }
38 |
39 | .center {
40 | text-align: center;
41 | }
42 |
43 | .brand {
44 | font-family: 'Playfair Display';
45 | color: var(--clrNeutral100-f9191126);
46 | font-size: 2rem;
47 | font-weight: 700;
48 | }
49 |
50 | .brand:focus {
51 | border-radius: 0.3em;
52 | outline-color: var(--clrNeutral100-f9191126);
53 | outline-offset: 0.3rem;
54 | outline-style: solid;
55 | outline-width: 0.2rem;
56 | }
57 |
58 | .brand-image {
59 | width: 120px;
60 | height: 93px;
61 | }
62 |
63 | .navmenu {
64 | display: flex;
65 | justify-content: flex-end;
66 | }
67 |
68 | @media screen and (max-width: 991px) {
69 | .navmenu {
70 | padding: 2rem;
71 | background-color: var(--clrPrimary600-aeaf7110);
72 | }
73 | }
74 |
75 | @media screen and (max-width: 767px) {
76 | .navmenu {
77 | flex-direction: column;
78 | align-items: center;
79 | }
80 | }
81 |
82 | .navlist {
83 | display: flex;
84 | margin-top: 0px;
85 | margin-bottom: 0px;
86 | padding-left: 0px;
87 | grid-column-gap: 1em;
88 |
89 | list-style-type: none;
90 | }
91 |
92 | @media screen and (max-width: 991px) {
93 | .navlist {
94 | flex-direction: column;
95 | align-items: center;
96 | }
97 | }
98 |
99 | .navlist-item {
100 | margin-bottom: 0em;
101 | }
102 |
103 | .navlink {
104 | padding: 1em 0.5em;
105 |
106 | box-shadow: inset 0 0 0 0 var(--clrSecondary400-685c3959);
107 |
108 | transition: color 200ms ease,box-shadow 200ms ease;
109 |
110 | color: var(--clrNeutral100-f9191126);
111 | font-size: 1.2rem;
112 | font-weight: 600;
113 | }
114 |
115 | .navlink:hover {
116 | box-shadow: inset 0 -5px 0 0 var(--clrSecondary400-685c3959);
117 | color: var(--clrSecondary400-685c3959);
118 | }
119 |
120 | .navlink:focus {
121 | border-radius: 0em;
122 | outline-color: var(--clrNeutral100-f9191126);
123 | outline-offset: 0.3rem;
124 | outline-style: solid;
125 | outline-width: 0.2rem;
126 | }
127 |
128 | .navlink:focus-visible {
129 | outline-color: var(--clrNeutral100-f9191126);
130 | outline-offset: 0.3rem;
131 | outline-style: solid;
132 | outline-width: 0.2rem;
133 | }
134 |
135 | .navlink:global(.w--current) {
136 | color: var(--clrNeutral100-f9191126);
137 | }
138 |
139 | .navlink:global(.w--current):hover {
140 | color: var(--clrSecondary400-685c3959);
141 | }
142 |
143 | @media screen and (max-width: 767px) {
144 | .navlink {
145 | text-align: center;
146 | }
147 | }
148 |
149 | .navlink.nav-button {
150 | padding-right: 2em;
151 | padding-left: 2em;
152 |
153 | border-radius: 2em;
154 |
155 | background-color: var(--clrQuarternary400-68656429);
156 |
157 | box-shadow: none;
158 |
159 | transition-property: none;
160 |
161 | color: var(--clrPrimary600-aeaf7110);
162 | }
163 |
164 | .navlink.nav-button:hover {
165 | background-color: var(--clrSecondary400-685c3959);
166 | color: var(--clrPrimary600-aeaf7110);
167 | }
168 |
169 | .navlink.nav-button:focus {
170 | outline-color: var(--clrQuarternary400-68656429);
171 | }
172 |
173 | @media screen and (max-width: 991px) {
174 | .hamburger {
175 | background-color: var(--clrPrimary600-aeaf7110);
176 | font-size: 2.5rem;
177 | }
178 | }
179 |
180 | @media screen and (max-width: 991px) {
181 | .hamburger:global(.w--open) {
182 | background-color: var(--clrPrimary400-6c00fdf1);
183 | color: var(--clrPrimary600-aeaf7110);
184 | }
185 | }
--------------------------------------------------------------------------------
/devlink/PageHeading.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as Types from "./types";
3 |
4 | declare function PageHeading(props: {
5 | as?: React.ElementType;
6 | headingText?: React.ReactNode;
7 | }): React.JSX.Element;
8 |
--------------------------------------------------------------------------------
/devlink/PageHeading.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _utils from "./utils";
4 | import _styles from "./PageHeading.module.css";
5 |
6 | export function PageHeading({
7 | as: _Component = _Builtin.Section,
8 | headingText = "Heading",
9 | }) {
10 | return (
11 | <_Component className={_utils.cx(_styles, "section", "title")} tag="div">
12 | <_Builtin.Container className={_utils.cx(_styles, "container")} tag="div">
13 | <_Builtin.Heading
14 | className={_utils.cx(_styles, "title-heading")}
15 | tag="h1"
16 | >
17 | {headingText}
18 |
19 |
20 |
21 | );
22 | }
23 |
--------------------------------------------------------------------------------
/devlink/PageHeading.module.css:
--------------------------------------------------------------------------------
1 | .section {
2 | padding-top: 5em;
3 | padding-bottom: 5em;
4 | }
5 |
6 | .section.hero {
7 | position: relative;
8 | min-height: 600px;
9 | background-color: var(--clrPrimary600-aeaf7110);
10 | color: var(--clrNeutral100-f9191126);
11 | }
12 |
13 | .section.title {
14 | background-color: var(--clrPrimary400-6c00fdf1);
15 | color: var(--clrPrimary999-b0a60ce1);
16 | }
17 |
18 | .section.pricing-gradient {
19 | background-image: radial-gradient(circle farthest-corner at 50% 100%, var(--clrPrimary400-6c00fdf1), var(--clrPrimary999-b0a60ce1) 73%);
20 | color: var(--clrNeutral100-f9191126);
21 | }
22 |
23 | .section.pricing {
24 | position: relative;
25 | padding-top: 5em;
26 | color: hsla(329.9999999999999, 0.00%, 100.00%, 1.00);
27 | text-align: center;
28 | }
29 |
30 | .clr-secondary-400 {
31 | background-color: var(--clrSecondary400-685c3959);
32 | }
33 |
34 | .clr-primary-400 {
35 | background-color: var(--clrPrimary400-6c00fdf1);
36 | }
37 |
38 | .section.features {
39 | color: var(--clrPrimary600-aeaf7110);
40 | }
41 |
42 | .container {
43 | width: 100%;
44 | max-width: 1200px;
45 | margin-right: auto;
46 | margin-left: auto;
47 | padding-right: 1em;
48 | padding-left: 1em;
49 | }
50 |
51 | @media screen and (max-width: 767px) {
52 | .container {
53 | padding-right: 1.5em;
54 | padding-left: 1.5em;
55 | }
56 | }
57 |
58 | .container.nav {
59 | display: flex;
60 | justify-content: space-between;
61 | align-items: center;
62 | }
63 |
64 | .container.narrow {
65 | max-width: 720px;
66 | }
67 |
68 | .container.rel {
69 | position: relative;
70 | z-index: 2;
71 | }
72 |
73 | .center {
74 | text-align: center;
75 | }
--------------------------------------------------------------------------------
/devlink/Pricing.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as Types from "./types";
3 |
4 | declare function Pricing(props: {
5 | as?: React.ElementType;
6 | employerPrice?: React.ReactNode;
7 | pricingBottom?: Types.Devlink.Slot;
8 | applicantPricing?: React.ReactNode;
9 | }): React.JSX.Element;
10 |
--------------------------------------------------------------------------------
/devlink/Pricing.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _interactions from "./interactions";
4 | import * as _utils from "./utils";
5 | import _styles from "./Pricing.module.css";
6 |
7 | const _interactionsData = JSON.parse(
8 | '{"events":{"e":{"id":"e","name":"","animationType":"preset","eventTypeId":"SCROLL_INTO_VIEW","action":{"id":"","actionTypeId":"SLIDE_EFFECT","instant":false,"config":{"actionListId":"slideInBottom","autoStopEventId":"e-2"}},"mediaQueries":["main","medium","small","tiny"],"target":{"id":"3fb202a6-7504-b84c-fbff-e674556ba402","appliesTo":"ELEMENT","styleBlockIds":[]},"targets":[{"id":"3fb202a6-7504-b84c-fbff-e674556ba402","appliesTo":"ELEMENT","styleBlockIds":[]}],"config":{"loop":false,"playInReverse":false,"scrollOffsetValue":35,"scrollOffsetUnit":"%","delay":0,"direction":"BOTTOM","effectIn":true},"createdOn":1684527451652}},"actionLists":{"slideInBottom":{"id":"slideInBottom","useFirstGroupAsInitialState":true,"actionItemGroups":[{"actionItems":[{"actionTypeId":"STYLE_OPACITY","config":{"delay":0,"duration":0,"target":{"id":"N/A","appliesTo":"TRIGGER_ELEMENT","useEventTarget":true},"value":0}}]},{"actionItems":[{"actionTypeId":"TRANSFORM_MOVE","config":{"delay":0,"duration":0,"target":{"id":"N/A","appliesTo":"TRIGGER_ELEMENT","useEventTarget":true},"xValue":0,"yValue":100,"xUnit":"PX","yUnit":"PX","zUnit":"PX"}}]},{"actionItems":[{"actionTypeId":"TRANSFORM_MOVE","config":{"delay":0,"easing":"outQuart","duration":1000,"target":{"id":"N/A","appliesTo":"TRIGGER_ELEMENT","useEventTarget":true},"xValue":0,"yValue":0,"xUnit":"PX","yUnit":"PX","zUnit":"PX"}},{"actionTypeId":"STYLE_OPACITY","config":{"delay":0,"easing":"outQuart","duration":1000,"target":{"id":"N/A","appliesTo":"TRIGGER_ELEMENT","useEventTarget":true},"value":1}}]}]}},"site":{"mediaQueries":[{"key":"main","min":992,"max":10000},{"key":"medium","min":768,"max":991},{"key":"small","min":480,"max":767},{"key":"tiny","min":0,"max":479}]}}'
9 | );
10 |
11 | export function Pricing({
12 | as: _Component = _Builtin.Section,
13 | employerPrice = "$99",
14 | pricingBottom,
15 | applicantPricing = "$9",
16 | }) {
17 | _interactions.useInteractions(_interactionsData, _styles);
18 |
19 | return (
20 | <_Component
21 | className={_utils.cx(_styles, "section", "pricing")}
22 | tag="section"
23 | >
24 | <_Builtin.Block
25 | className={_utils.cx(_styles, "radial-gradient")}
26 | tag="div"
27 | />
28 | <_Builtin.Container
29 | className={_utils.cx(_styles, "container", "rel")}
30 | tag="div"
31 | >
32 | <_Builtin.Heading tag="h2">
33 | {"Find the right price for you"}
34 |
35 | <_Builtin.Paragraph className={_utils.cx(_styles, "mw-70ch")}>
36 | {
37 | "Explore our flexible pricing options designed to meet the diverse needs of both job seekers and employers. Unlock the full potential of the Visual Developers Job Board and invest in your future success with our affordable and value-driven plans."
38 | }
39 |
40 |
41 | <_Builtin.Container
42 | className={_utils.cx(_styles, "container", "rel")}
43 | tag="div"
44 | >
45 | <_Builtin.Block
46 | className={_utils.cx(_styles, "two-columns", "narrow")}
47 | tag="div"
48 | >
49 | <_Builtin.Block
50 | className={_utils.cx(_styles, "pricing-card-large")}
51 | id={_utils.cx(
52 | _styles,
53 | "w-node-_3fb202a6-7504-b84c-fbff-e674556ba3c8-556ba3bf"
54 | )}
55 | tag="div"
56 | >
57 | <_Builtin.Block
58 | className={_utils.cx(_styles, "pricing-tag")}
59 | tag="div"
60 | >
61 | {"Employers"}
62 |
63 | <_Builtin.Block
64 | className={_utils.cx(_styles, "price-text-wrapper")}
65 | tag="div"
66 | >
67 | <_Builtin.Block className={_utils.cx(_styles, "price")} tag="div">
68 | {employerPrice}
69 |
70 | <_Builtin.Block tag="div">{"/month"}
71 |
72 | <_Builtin.Paragraph className={_utils.cx(_styles, "p-small")}>
73 | {
74 | "Our Employers Plan is tailored to provide maximum visibility and engagement for your job listings, ensuring you reach the most talented and qualified candidates in the development community."
75 | }
76 |
77 | <_Builtin.List
78 | className={_utils.cx(_styles, "pricing-list")}
79 | tag="ul"
80 | unstyled={false}
81 | >
82 | <_Builtin.ListItem>
83 | {
84 | "Priority placement of your job listings, resulting in increased visibility"
85 | }
86 |
87 | <_Builtin.ListItem>
88 | {"Customizable job posts with your company logo and branding"}
89 |
90 | <_Builtin.ListItem>
91 | {
92 | "Access to our curated pool of highly skilled Webflow, no-code, and traditional developers"
93 | }
94 |
95 | <_Builtin.ListItem>
96 | {
97 | "Analytics dashboard to track the performance of your job listings"
98 | }
99 |
100 | <_Builtin.ListItem>
101 | {
102 | "Dedicated account manager for personalized support and guidance"
103 | }
104 |
105 | <_Builtin.ListItem>
106 | {
107 | "Social media promotion of your job posts to extend your reach"
108 | }
109 |
110 | <_Builtin.ListItem>
111 | {"Unlimited job edits and updates during the listing period"}
112 |
113 | <_Builtin.ListItem>
114 | {"Discounted bulk pricing for multiple job listings"}
115 |
116 |
117 | <_Builtin.Link
118 | className={_utils.cx(_styles, "button", "sign-up")}
119 | button={true}
120 | options={{
121 | href: "#",
122 | }}
123 | >
124 | {"Sign up"}
125 |
126 |
127 | <_Builtin.Block
128 | className={_utils.cx(_styles, "pricing-card-large")}
129 | id={_utils.cx(
130 | _styles,
131 | "w-node-_3fb202a6-7504-b84c-fbff-e674556ba3e5-556ba3bf"
132 | )}
133 | tag="div"
134 | >
135 | <_Builtin.Block
136 | className={_utils.cx(_styles, "pricing-tag")}
137 | tag="div"
138 | >
139 | {"Applicants"}
140 |
141 | <_Builtin.Block
142 | className={_utils.cx(_styles, "price-text-wrapper")}
143 | tag="div"
144 | >
145 | <_Builtin.Block className={_utils.cx(_styles, "price")} tag="div">
146 | {applicantPricing}
147 |
148 | <_Builtin.Block tag="div">{"/month"}
149 |
150 | <_Builtin.Paragraph className={_utils.cx(_styles, "p-small")}>
151 | {
152 | "Our Applicants Plan is designed to give you a competitive edge in your job search, connecting you with exclusive opportunities and resources to accelerate your career in development."
153 | }
154 |
155 | <_Builtin.List
156 | className={_utils.cx(_styles, "pricing-list")}
157 | tag="ul"
158 | unstyled={false}
159 | >
160 | <_Builtin.ListItem>
161 | {
162 | "Early access to the latest job listings before they're publicly available"
163 | }
164 |
165 | <_Builtin.ListItem>
166 | {
167 | "Personalized job recommendations based on your skills and preferences"
168 | }
169 |
170 | <_Builtin.ListItem>
171 | {
172 | "Access to a library of resources, including tutorials, articles, and webinars to enhance your skills"
173 | }
174 |
175 | <_Builtin.ListItem>
176 | {
177 | "Profile optimization tips to help you stand out from the crowd"
178 | }
179 |
180 | <_Builtin.ListItem>
181 | {
182 | "Customizable job alerts to ensure you never miss a relevant opportunity"
183 | }
184 |
185 | <_Builtin.ListItem>
186 | {
187 | "Networking opportunities with fellow developers and industry professionals"
188 | }
189 |
190 | <_Builtin.ListItem>
191 | {
192 | "Priority support from our dedicated team to assist with your job search"
193 | }
194 |
195 | <_Builtin.ListItem>
196 | {
197 | "Exclusive discounts on relevant tools, courses, and events to support your professional growth"
198 | }
199 |
200 |
201 | <_Builtin.Link
202 | className={_utils.cx(_styles, "button", "sign-up")}
203 | button={true}
204 | options={{
205 | href: "#",
206 | }}
207 | >
208 | {"Sign up"}
209 |
210 |
211 |
212 | <_Builtin.Block
213 | className={_utils.cx(_styles, "pricing-bottom")}
214 | data-w-id="3fb202a6-7504-b84c-fbff-e674556ba402"
215 | tag="div"
216 | >
217 | {pricingBottom}
218 |
219 |
220 |
221 | );
222 | }
223 |
--------------------------------------------------------------------------------
/devlink/Pricing.module.css:
--------------------------------------------------------------------------------
1 | .section {
2 | padding-top: 5em;
3 | padding-bottom: 5em;
4 | }
5 |
6 | .section.hero {
7 | position: relative;
8 | min-height: 600px;
9 | background-color: var(--clrPrimary600-aeaf7110);
10 | color: var(--clrNeutral100-f9191126);
11 | }
12 |
13 | .section.title {
14 | background-color: var(--clrPrimary400-6c00fdf1);
15 | color: var(--clrPrimary999-b0a60ce1);
16 | }
17 |
18 | .section.pricing-gradient {
19 | background-image: radial-gradient(circle farthest-corner at 50% 100%, var(--clrPrimary400-6c00fdf1), var(--clrPrimary999-b0a60ce1) 73%);
20 | color: var(--clrNeutral100-f9191126);
21 | }
22 |
23 | .section.pricing {
24 | position: relative;
25 | padding-top: 5em;
26 | color: hsla(329.9999999999999, 0.00%, 100.00%, 1.00);
27 | text-align: center;
28 | }
29 |
30 | .clr-secondary-400 {
31 | background-color: var(--clrSecondary400-685c3959);
32 | }
33 |
34 | .clr-primary-400 {
35 | background-color: var(--clrPrimary400-6c00fdf1);
36 | }
37 |
38 | .section.features {
39 | color: var(--clrPrimary600-aeaf7110);
40 | }
41 |
42 | .radial-gradient {
43 | position: absolute;
44 | left: 0%;
45 | top: 0%;
46 | right: 0%;
47 | bottom: 0%;
48 | z-index: 0;
49 |
50 | height: 60%;
51 | padding-top: 5em;
52 |
53 | background-image: radial-gradient(circle farthest-side at 50% 140%, var(--clrPrimary400-6c00fdf1), var(--clrPrimary600-aeaf7110) 59%, var(--clrPrimary999-b0a60ce1));
54 |
55 | color: var(--clrNeutral100-f9191126);
56 | text-align: center;
57 | }
58 |
59 | .container {
60 | width: 100%;
61 | max-width: 1200px;
62 | margin-right: auto;
63 | margin-left: auto;
64 | padding-right: 1em;
65 | padding-left: 1em;
66 | }
67 |
68 | @media screen and (max-width: 767px) {
69 | .container {
70 | padding-right: 1.5em;
71 | padding-left: 1.5em;
72 | }
73 | }
74 |
75 | .container.nav {
76 | display: flex;
77 | justify-content: space-between;
78 | align-items: center;
79 | }
80 |
81 | .container.narrow {
82 | max-width: 720px;
83 | }
84 |
85 | .container.rel {
86 | position: relative;
87 | z-index: 2;
88 | }
89 |
90 | .center {
91 | text-align: center;
92 | }
93 |
94 | .mw-70ch {
95 | max-width: 70ch;
96 | margin-right: auto;
97 | margin-left: auto;
98 | }
99 |
100 | .mb2 {
101 | margin-bottom: 2rem;
102 | }
103 |
104 | .two-columns {
105 | display: grid;
106 | padding-left: 0px;
107 | grid-auto-columns: 1fr;
108 | grid-column-gap: 1.5rem;
109 | grid-row-gap: 1.5rem;
110 | grid-template-columns: 1fr 1fr;
111 | grid-template-rows: auto;
112 | }
113 |
114 | @media screen and (max-width: 767px) {
115 | .two-columns {
116 | grid-template-columns: 1fr;
117 | }
118 | }
119 |
120 | .two-columns.narrow {
121 | max-width: 960px;
122 | margin: 3em auto;
123 | }
124 |
125 | .pricing-card-large {
126 | display: flex;
127 | padding: 2em;
128 | flex-direction: column;
129 | grid-row-gap: 1em;
130 |
131 | border-radius: 2em;
132 |
133 | background-color: hsla(210, 0.00%, 100.00%, 1.00);
134 |
135 | box-shadow: 0 1px 11px 5px hsla(243.75, 43.24%, 14.51%, 0.32);
136 |
137 | color: var(--clrNeutral900-23d5b0c0);
138 | text-align: left;
139 | }
140 |
141 | @media screen and (max-width: 767px) {
142 | .pricing-card-large {
143 | margin-top: 0em;
144 | }
145 | }
146 |
147 | .pricing-tag {
148 | color: var(--clrPrimary600-aeaf7110);
149 | font-weight: 600;
150 | }
151 |
152 | .price-text-wrapper {
153 | display: flex;
154 | align-items: flex-end;
155 | }
156 |
157 | .price {
158 | font-size: 3rem;
159 | line-height: 1;
160 | font-weight: 700;
161 | }
162 |
163 | .p-small {
164 | font-size: 1rem;
165 | }
166 |
167 | .pricing-list {
168 | font-size: 0.9rem;
169 | }
170 |
171 | .button {
172 | padding: 1em 2em;
173 |
174 | border-radius: 2em;
175 |
176 | background-color: hsla(247.6190476190476, 42.28%, 29.22%, 1.00);
177 |
178 | box-shadow: none;
179 |
180 | transition: background-color 200ms ease,color 200ms ease;
181 |
182 | color: hsla(247.05882352941174, 0.00%, 100.00%, 1.00);
183 | font-size: 1.3rem;
184 | font-weight: 700;
185 | }
186 |
187 | .button:hover {
188 | background-color: hsla(196.55172413793105, 54.72%, 79.22%, 1.00);
189 | box-shadow: none;
190 | color: hsla(247.6190476190476, 42.28%, 29.22%, 1.00);
191 | }
192 |
193 | .button:focus {
194 | outline-color: var(--clrPrimary600-aeaf7110);
195 | outline-offset: 0.2rem;
196 | outline-style: solid;
197 | outline-width: 0.2rem;
198 | }
199 |
200 | .button:focus-visible {
201 | outline-color: var(--clrPrimary600-aeaf7110);
202 | outline-offset: 0.2rem;
203 | outline-style: solid;
204 | outline-width: 0.2rem;
205 | }
206 |
207 | .button.secondary {
208 | background-color: var(--clrSecondary400-685c3959);
209 | color: var(--clrPrimary600-aeaf7110);
210 | }
211 |
212 | .button.secondary:hover {
213 | background-color: var(--clrPrimary400-6c00fdf1);
214 | color: var(--clrNeutral100-f9191126);
215 | }
216 |
217 | .button.secondary:focus {
218 | outline-color: var(--clrSecondary400-685c3959);
219 | }
220 |
221 | .button.tertiary {
222 | background-color: var(--clrQuarternary400-68656429);
223 | color: var(--clrPrimary600-aeaf7110);
224 | }
225 |
226 | .button.tertiary:hover {
227 | background-color: var(--clrPrimary600-aeaf7110);
228 | color: var(--clrNeutral100-f9191126);
229 | }
230 |
231 | .button.tertiary:focus {
232 | outline-color: var(--clrQuarternary400-68656429);
233 | }
234 |
235 | .button.small {
236 | padding: 1em 1.5em;
237 | font-size: 1rem;
238 | }
239 |
240 | .button.sign-up {
241 | text-align: center;
242 | }
243 |
244 | .pricing-bottom {
245 | max-width: 960px;
246 | margin-right: auto;
247 | margin-left: auto;
248 | padding: 2em;
249 |
250 | border: 1px solid var(--clrNuetral500-69afe2ca);
251 | border-radius: 2em;
252 | }
253 |
254 | #w-node-_3fb202a6-7504-b84c-fbff-e674556ba3c8-556ba3bf {
255 | grid-column-start: span 1;
256 | grid-column-end: span 1;
257 | grid-row-start: span 1;
258 | grid-row-end: span 1;
259 | }
260 | #w-node-_3fb202a6-7504-b84c-fbff-e674556ba3e5-556ba3bf {
261 | grid-column-start: span 1;
262 | grid-column-end: span 1;
263 | grid-row-start: span 1;
264 | grid-row-end: span 1;
265 | }
--------------------------------------------------------------------------------
/devlink/PricingGrid.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as Types from "./types";
3 |
4 | declare function PricingGrid(props: {
5 | as?: React.ElementType;
6 | pricingHeading?: React.ReactNode;
7 | }): React.JSX.Element;
8 |
--------------------------------------------------------------------------------
/devlink/PricingGrid.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _utils from "./utils";
4 | import _styles from "./PricingGrid.module.css";
5 |
6 | export function PricingGrid({
7 | as: _Component = _Builtin.Block,
8 | pricingHeading = "Student plans",
9 | }) {
10 | return (
11 | <_Component className={_utils.cx(_styles, "pricing-bottom-grid")} tag="div">
12 | <_Builtin.Block
13 | id={_utils.cx(
14 | _styles,
15 | "w-node-_05a0cdfd-5b52-43c7-186d-fe226e527f86-6e527f85"
16 | )}
17 | tag="div"
18 | >
19 | <_Builtin.Heading tag="h3">{pricingHeading}
20 | <_Builtin.Paragraph className={_utils.cx(_styles, "p-small")}>
21 | {
22 | "Discover our Student Plan, specially designed to support the next generation of developers by providing exclusive resources, opportunities, and discounts tailored to your educational journey."
23 | }
24 |
25 |
26 | <_Builtin.Block
27 | id={_utils.cx(
28 | _styles,
29 | "w-node-_05a0cdfd-5b52-43c7-186d-fe226e527f8b-6e527f85"
30 | )}
31 | tag="div"
32 | >
33 | <_Builtin.Link
34 | className={_utils.cx(_styles, "button", "tertiary")}
35 | button={true}
36 | options={{
37 | href: "#",
38 | }}
39 | >
40 | {"Get started"}
41 |
42 |
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/devlink/PricingGrid.module.css:
--------------------------------------------------------------------------------
1 | .pricing-bottom-grid {
2 | display: grid;
3 | padding-left: 0px;
4 | grid-auto-columns: 1fr;
5 | grid-column-gap: 1.5rem;
6 | grid-row-gap: 1.5rem;
7 | grid-template-columns: 1.75fr 1fr;
8 | grid-template-rows: auto;
9 |
10 | color: var(--clrNeutral900-23d5b0c0);
11 | text-align: left;
12 | }
13 |
14 | @media screen and (max-width: 767px) {
15 | .pricing-bottom-grid {
16 | grid-template-columns: 1fr;
17 | }
18 | }
19 |
20 | .pricing-bottom-grid.narrow {
21 | max-width: 960px;
22 | margin: 3em auto;
23 | }
24 |
25 | .p-small {
26 | font-size: 1rem;
27 | }
28 |
29 | .button {
30 | padding: 1em 2em;
31 |
32 | border-radius: 2em;
33 |
34 | background-color: hsla(247.6190476190476, 42.28%, 29.22%, 1.00);
35 |
36 | box-shadow: none;
37 |
38 | transition: background-color 200ms ease,color 200ms ease;
39 |
40 | color: hsla(247.05882352941174, 0.00%, 100.00%, 1.00);
41 | font-size: 1.3rem;
42 | font-weight: 700;
43 | }
44 |
45 | .button:hover {
46 | background-color: hsla(196.55172413793105, 54.72%, 79.22%, 1.00);
47 | box-shadow: none;
48 | color: hsla(247.6190476190476, 42.28%, 29.22%, 1.00);
49 | }
50 |
51 | .button:focus {
52 | outline-color: var(--clrPrimary600-aeaf7110);
53 | outline-offset: 0.2rem;
54 | outline-style: solid;
55 | outline-width: 0.2rem;
56 | }
57 |
58 | .button:focus-visible {
59 | outline-color: var(--clrPrimary600-aeaf7110);
60 | outline-offset: 0.2rem;
61 | outline-style: solid;
62 | outline-width: 0.2rem;
63 | }
64 |
65 | .button.secondary {
66 | background-color: var(--clrSecondary400-685c3959);
67 | color: var(--clrPrimary600-aeaf7110);
68 | }
69 |
70 | .button.secondary:hover {
71 | background-color: var(--clrPrimary400-6c00fdf1);
72 | color: var(--clrNeutral100-f9191126);
73 | }
74 |
75 | .button.secondary:focus {
76 | outline-color: var(--clrSecondary400-685c3959);
77 | }
78 |
79 | .button.tertiary {
80 | background-color: var(--clrQuarternary400-68656429);
81 | color: var(--clrPrimary600-aeaf7110);
82 | }
83 |
84 | .button.tertiary:hover {
85 | background-color: var(--clrPrimary600-aeaf7110);
86 | color: var(--clrNeutral100-f9191126);
87 | }
88 |
89 | .button.tertiary:focus {
90 | outline-color: var(--clrQuarternary400-68656429);
91 | }
92 |
93 | .button.small {
94 | padding: 1em 1.5em;
95 | font-size: 1rem;
96 | }
97 |
98 | .button.sign-up {
99 | text-align: center;
100 | }
101 |
102 | #w-node-_05a0cdfd-5b52-43c7-186d-fe226e527f86-6e527f85 {
103 | grid-column-start: span 1;
104 | grid-column-end: span 1;
105 | grid-row-start: span 1;
106 | grid-row-end: span 1;
107 | align-self: center;
108 | }
109 | #w-node-_05a0cdfd-5b52-43c7-186d-fe226e527f8b-6e527f85 {
110 | grid-column-start: span 1;
111 | grid-column-end: span 1;
112 | grid-row-start: span 1;
113 | grid-row-end: span 1;
114 | justify-self: end;
115 | align-self: center;
116 | }
117 | @media screen and (max-width: 767px) {
118 | #w-node-_05a0cdfd-5b52-43c7-186d-fe226e527f8b-6e527f85 {
119 | justify-self: start;
120 | }
121 | }
--------------------------------------------------------------------------------
/devlink/Stats.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as Types from "./types";
3 |
4 | declare function Stats(props: {
5 | as?: React.ElementType;
6 | chart?: Types.Devlink.Slot;
7 | }): React.JSX.Element;
8 |
--------------------------------------------------------------------------------
/devlink/Stats.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as _Builtin from "./_Builtin";
3 | import * as _utils from "./utils";
4 | import _styles from "./Stats.module.css";
5 |
6 | export function Stats({ as: _Component = _Builtin.Section, chart }) {
7 | return (
8 | <_Component className={_utils.cx(_styles, "section")} tag="section">
9 | <_Builtin.Container className={_utils.cx(_styles, "container")} tag="div">
10 | <_Builtin.Heading className={_utils.cx(_styles, "tl-heading")} tag="h2">
11 | {"Average time to fill an open role"}
12 |
13 | <_Builtin.Block
14 | className={_utils.cx(_styles, "timeline-grid")}
15 | tag="div"
16 | >
17 | <_Builtin.Block
18 | className={_utils.cx(_styles, "tl-card")}
19 | id={_utils.cx(
20 | _styles,
21 | "w-node-_13e882f5-b194-bda1-60df-b8cbb049f2ee-b049f2e9"
22 | )}
23 | tag="div"
24 | >
25 | <_Builtin.Block
26 | className={_utils.cx(_styles, "tl-line-wrapper")}
27 | tag="div"
28 | >
29 | <_Builtin.Block tag="div">{"•"}
30 | <_Builtin.Block tag="div">{"Day 1"}
31 | <_Builtin.Block
32 | className={_utils.cx(_styles, "tl-line")}
33 | tag="div"
34 | />
35 |
36 | <_Builtin.Block tag="div">
37 | <_Builtin.Block
38 | className={_utils.cx(_styles, "tl-title")}
39 | tag="div"
40 | >
41 | {"Post your open role"}
42 |
43 | <_Builtin.Paragraph className={_utils.cx(_styles, "tl-summary")}>
44 | {
45 | "Streamline your hiring process by quickly posting your open roles on our platform, reaching a vast network of highly skilled Webflow, no-code, and traditional developers."
46 | }
47 |
48 |
49 |
50 | <_Builtin.Block
51 | className={_utils.cx(_styles, "tl-card")}
52 | id={_utils.cx(
53 | _styles,
54 | "w-node-_13e882f5-b194-bda1-60df-b8cbb049f2fa-b049f2e9"
55 | )}
56 | tag="div"
57 | >
58 | <_Builtin.Block
59 | className={_utils.cx(_styles, "tl-line-wrapper")}
60 | tag="div"
61 | >
62 | <_Builtin.Block tag="div">{"•"}
63 | <_Builtin.Block tag="div">{"Days 2 - 4"}
64 | <_Builtin.Block
65 | className={_utils.cx(_styles, "tl-line")}
66 | tag="div"
67 | />
68 |
69 | <_Builtin.Block tag="div">
70 | <_Builtin.Block
71 | className={_utils.cx(_styles, "tl-title")}
72 | tag="div"
73 | >
74 | {"Review incoming applicants"}
75 |
76 | <_Builtin.Paragraph className={_utils.cx(_styles, "tl-summary")}>
77 | {
78 | "Save time and effort by conveniently reviewing all incoming applications in one place, facilitated by our intuitive interface and applicant sorting features."
79 | }
80 |
81 |
82 |
83 | <_Builtin.Block
84 | className={_utils.cx(_styles, "tl-card")}
85 | id={_utils.cx(
86 | _styles,
87 | "w-node-_13e882f5-b194-bda1-60df-b8cbb049f306-b049f2e9"
88 | )}
89 | tag="div"
90 | >
91 | <_Builtin.Block
92 | className={_utils.cx(_styles, "tl-line-wrapper")}
93 | tag="div"
94 | >
95 | <_Builtin.Block tag="div">{"•"}
96 | <_Builtin.Block tag="div">{"Day 5"}
97 | <_Builtin.Block
98 | className={_utils.cx(_styles, "tl-line")}
99 | tag="div"
100 | />
101 |
102 | <_Builtin.Block tag="div">
103 | <_Builtin.Block
104 | className={_utils.cx(_styles, "tl-title")}
105 | tag="div"
106 | >
107 | {"Set up interview times"}
108 |
109 | <_Builtin.Paragraph className={_utils.cx(_styles, "tl-summary")}>
110 | {
111 | "Take advantage of our integrated scheduling tool to effortlessly set up interviews, ensuring a smooth and efficient selection process that respects everyone's time."
112 | }
113 |
114 |
115 |
116 | <_Builtin.Block
117 | className={_utils.cx(_styles, "tl-card")}
118 | id={_utils.cx(
119 | _styles,
120 | "w-node-_13e882f5-b194-bda1-60df-b8cbb049f312-b049f2e9"
121 | )}
122 | tag="div"
123 | >
124 | <_Builtin.Block
125 | className={_utils.cx(_styles, "tl-line-wrapper")}
126 | tag="div"
127 | >
128 | <_Builtin.Block tag="div">{"•"}
129 | <_Builtin.Block tag="div">{"Day 7"}
130 | <_Builtin.Block
131 | className={_utils.cx(_styles, "tl-line")}
132 | tag="div"
133 | />
134 |
135 | <_Builtin.Block tag="div">
136 | <_Builtin.Block
137 | className={_utils.cx(_styles, "tl-title")}
138 | tag="div"
139 | >
140 | {"Send out an offer"}
141 |
142 | <_Builtin.Paragraph className={_utils.cx(_styles, "tl-summary")}>
143 | {
144 | "Once you've found your ideal candidate, our platform makes it easy to send out an offer, accelerating the hiring timeline and helping you build your dream team faster."
145 | }
146 |
147 |
148 |
149 |
150 | <_Builtin.Heading className={_utils.cx(_styles, "tl-heading")} tag="h2">
151 | {"Average time to hire visual"}
152 |
153 | <_Builtin.Block tag="div">{chart}
154 |
155 |
156 | );
157 | }
158 |
--------------------------------------------------------------------------------
/devlink/Stats.module.css:
--------------------------------------------------------------------------------
1 | .section {
2 | padding-top: 5em;
3 | padding-bottom: 5em;
4 | }
5 |
6 | .section.hero {
7 | position: relative;
8 | min-height: 600px;
9 | background-color: var(--clrPrimary600-aeaf7110);
10 | color: var(--clrNeutral100-f9191126);
11 | }
12 |
13 | .section.title {
14 | background-color: var(--clrPrimary400-6c00fdf1);
15 | color: var(--clrPrimary999-b0a60ce1);
16 | }
17 |
18 | .section.pricing-gradient {
19 | background-image: radial-gradient(circle farthest-corner at 50% 100%, var(--clrPrimary400-6c00fdf1), var(--clrPrimary999-b0a60ce1) 73%);
20 | color: var(--clrNeutral100-f9191126);
21 | }
22 |
23 | .section.pricing {
24 | position: relative;
25 | padding-top: 5em;
26 | color: hsla(329.9999999999999, 0.00%, 100.00%, 1.00);
27 | text-align: center;
28 | }
29 |
30 | .clr-secondary-400 {
31 | background-color: var(--clrSecondary400-685c3959);
32 | }
33 |
34 | .clr-primary-400 {
35 | background-color: var(--clrPrimary400-6c00fdf1);
36 | }
37 |
38 | .section.features {
39 | color: var(--clrPrimary600-aeaf7110);
40 | }
41 |
42 | .container {
43 | width: 100%;
44 | max-width: 1200px;
45 | margin-right: auto;
46 | margin-left: auto;
47 | padding-right: 1em;
48 | padding-left: 1em;
49 | }
50 |
51 | @media screen and (max-width: 767px) {
52 | .container {
53 | padding-right: 1.5em;
54 | padding-left: 1.5em;
55 | }
56 | }
57 |
58 | .container.nav {
59 | display: flex;
60 | justify-content: space-between;
61 | align-items: center;
62 | }
63 |
64 | .container.narrow {
65 | max-width: 720px;
66 | }
67 |
68 | .container.rel {
69 | position: relative;
70 | z-index: 2;
71 | }
72 |
73 | .center {
74 | text-align: center;
75 | }
76 |
77 | .tl-heading {
78 | margin-bottom: 2em;
79 | }
80 |
81 | .timeline-grid {
82 | display: grid;
83 | margin-bottom: 2em;
84 | grid-auto-columns: 1fr;
85 | grid-column-gap: 16px;
86 | grid-row-gap: 16px;
87 | grid-template-columns: 1fr 1fr 1fr 1fr;
88 | grid-template-rows: auto;
89 | }
90 |
91 | @media screen and (max-width: 767px) {
92 | .timeline-grid {
93 | grid-template-columns: 1fr 1fr;
94 | }
95 | }
96 |
97 | @media screen and (max-width: 479px) {
98 | .timeline-grid {
99 | grid-template-columns: 1fr;
100 | }
101 | }
102 |
103 | .tl-line-wrapper {
104 | display: flex;
105 | margin-bottom: 1em;
106 | align-items: center;
107 | grid-column-gap: 1em;
108 |
109 | color: var(--clrPrimary600-aeaf7110);
110 | font-size: 0.9rem;
111 | font-weight: 700;
112 | }
113 |
114 | .tl-line {
115 | height: 1px;
116 | flex: 1;
117 | border-top: 1px solid var(--clrPrimary600-aeaf7110);
118 | }
119 |
120 | .tl-title {
121 | margin-bottom: 1em;
122 | font-weight: 700;
123 | }
124 |
125 | .tl-summary {
126 | font-size: 1rem;
127 | }
128 |
129 | #w-node-_13e882f5-b194-bda1-60df-b8cbb049f2ee-b049f2e9 {
130 | grid-column-start: span 1;
131 | grid-column-end: span 1;
132 | grid-row-start: span 1;
133 | grid-row-end: span 1;
134 | }
135 | #w-node-_13e882f5-b194-bda1-60df-b8cbb049f2fa-b049f2e9 {
136 | grid-column-start: span 1;
137 | grid-column-end: span 1;
138 | grid-row-start: span 1;
139 | grid-row-end: span 1;
140 | }
141 | #w-node-_13e882f5-b194-bda1-60df-b8cbb049f306-b049f2e9 {
142 | grid-column-start: span 1;
143 | grid-column-end: span 1;
144 | grid-row-start: span 1;
145 | grid-row-end: span 1;
146 | }
147 | #w-node-_13e882f5-b194-bda1-60df-b8cbb049f312-b049f2e9 {
148 | grid-column-start: span 1;
149 | grid-column-end: span 1;
150 | grid-row-start: span 1;
151 | grid-row-end: span 1;
152 | }
--------------------------------------------------------------------------------
/devlink/_Builtin/BackgroundVideo.d.ts:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | declare type BackgroundVideoWrapperProps = {
3 | tag?: keyof HTMLElementTagNameMap;
4 | className?: string;
5 | sources?: string[];
6 | posterImage?: "";
7 | autoPlay?: boolean;
8 | loop?: boolean;
9 | children?: React.ReactElement;
10 | };
11 | export declare const BackgroundVideoWrapper: ({
12 | tag,
13 | className,
14 | autoPlay,
15 | loop,
16 | sources,
17 | posterImage,
18 | children,
19 | }: BackgroundVideoWrapperProps) => JSX.Element;
20 | declare type BackgroundVideoPlayPauseButtonProps = {
21 | className?: string;
22 | children: React.ReactElement<
23 | | BackgroundVideoPlayPauseButtonPlayingProps
24 | | BackgroundVideoPlayPauseButtonPausedProps
25 | >[];
26 | };
27 | export declare const BackgroundVideoPlayPauseButton: ({
28 | children,
29 | className,
30 | }: BackgroundVideoPlayPauseButtonProps) => JSX.Element;
31 | declare type BackgroundVideoPlayPauseButtonPlayingProps = {
32 | children: React.ReactNode;
33 | };
34 | export declare const BackgroundVideoPlayPauseButtonPlaying: ({
35 | children,
36 | }: BackgroundVideoPlayPauseButtonPlayingProps) => JSX.Element;
37 | declare type BackgroundVideoPlayPauseButtonPausedProps = {
38 | children: React.ReactNode;
39 | };
40 | export declare const BackgroundVideoPlayPauseButtonPaused: ({
41 | children,
42 | }: BackgroundVideoPlayPauseButtonPausedProps) => JSX.Element;
43 | export {};
44 |
--------------------------------------------------------------------------------
/devlink/_Builtin/BackgroundVideo.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { cj, debounce } from "../utils";
3 | const BgVideoContext = React.createContext({
4 | isPlaying: true,
5 | togglePlay: () => {},
6 | });
7 | export const BackgroundVideoWrapper = ({
8 | tag = "div",
9 | className = "",
10 | autoPlay = true,
11 | loop = true,
12 | sources = [],
13 | posterImage = "",
14 | children,
15 | }) => {
16 | const [isPlaying, setIsPlaying] = React.useState(autoPlay);
17 | const video = React.useRef(null);
18 | const togglePlay = debounce(() => {
19 | setIsPlaying(!isPlaying);
20 | if (!video?.current) return;
21 | if (video.current.paused) {
22 | video.current.play();
23 | } else {
24 | video.current.pause();
25 | }
26 | });
27 | return (
28 |
29 | {React.createElement(
30 | tag,
31 | {
32 | className: cj(
33 | className,
34 | "w-background-video",
35 | "w-background-video-atom"
36 | ),
37 | },
38 |
54 | )}
55 | {children}
56 |
57 | );
58 | };
59 | export const BackgroundVideoPlayPauseButton = ({ children, className }) => {
60 | const { togglePlay } = React.useContext(BgVideoContext);
61 | return (
62 |
63 |
74 |
75 | );
76 | };
77 | export const BackgroundVideoPlayPauseButtonPlaying = ({ children }) => {
78 | const { isPlaying } = React.useContext(BgVideoContext);
79 | return {children};
80 | };
81 | export const BackgroundVideoPlayPauseButtonPaused = ({ children }) => {
82 | const { isPlaying } = React.useContext(BgVideoContext);
83 | return {children};
84 | };
85 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Basic.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import * as utils from "../utils";
3 | export function Block({ tag = "div", ...props }) {
4 | return React.createElement(tag, props);
5 | }
6 | export function Span(props) {
7 | return ;
8 | }
9 | export function Blockquote(props) {
10 | return ;
11 | }
12 | export const Link = function Link({
13 | options = { href: "#" },
14 | className = "",
15 | button = false,
16 | ...props
17 | }) {
18 | if (button) className += " w-button";
19 | const { href, target, preload = "none" } = options;
20 | const shouldRenderResource =
21 | preload !== "none" && typeof href === "string" && !href.startsWith("#");
22 | return (
23 | <>
24 |
25 | {shouldRenderResource && }
26 | >
27 | );
28 | };
29 | export function List({
30 | tag = "ul",
31 | unstyled = true,
32 | className = "",
33 | ...props
34 | }) {
35 | return React.createElement(tag, {
36 | role: "list",
37 | className: className + (unstyled ? " w-list-unstyled" : ""),
38 | ...props,
39 | });
40 | }
41 | export function ListItem(props) {
42 | return React.createElement("li", props);
43 | }
44 | export function Image(props) {
45 | return React.createElement("img", props);
46 | }
47 | export function Section({ tag = "section", ...props }) {
48 | return React.createElement(tag, props);
49 | }
50 | export const Container = React.forwardRef(function Container(
51 | { tag = "div", className = "", ...props },
52 | ref
53 | ) {
54 | return React.createElement(tag, {
55 | className: className + " w-container",
56 | ref,
57 | ...props,
58 | });
59 | });
60 | export function HtmlEmbed({
61 | tag = "div",
62 | className = "",
63 | value = "",
64 | ...props
65 | }) {
66 | return React.createElement(tag, {
67 | className: className + " w-embed",
68 | dangerouslySetInnerHTML: { __html: utils.removeUnescaped(value) },
69 | ...props,
70 | });
71 | }
72 | export function Grid({ tag = "div", className = "", ...props }) {
73 | return React.createElement(tag, {
74 | className: className + " w-layout-grid",
75 | ...props,
76 | });
77 | }
78 | export function Icon({ widget, className = "", ...props }) {
79 | return React.createElement("div", {
80 | className: className + ` w-icon-${widget.icon}`,
81 | ...props,
82 | });
83 | }
84 | export function Column({
85 | tag = "div",
86 | className = "",
87 | columnClasses = "",
88 | ...props
89 | }) {
90 | return React.createElement(tag, {
91 | className: className + " w-col " + columnClasses,
92 | ...props,
93 | });
94 | }
95 | const transformWidths = (width, index) => {
96 | const widths = width?.split("|") ?? [];
97 | return widths.length > 1 ? widths[index] : width;
98 | };
99 | const columnClass = (width, key) => {
100 | if (/stack$/.test(width)) return "w-col-stack";
101 | if (/main$/.test(key)) return `w-col-${width}`;
102 | return `w-col-${key}-${width}`;
103 | };
104 | export function Row({ tag = "div", className = "", grid, children, ...props }) {
105 | return React.createElement(
106 | tag,
107 | { className: className + " w-row", ...props },
108 | grid
109 | ? React.Children.map(children, (child, index) => {
110 | if (child && typeof child === "object" && child.type !== Column)
111 | return child;
112 | const columnClasses = Object.entries(grid.cols ?? {}).reduce(
113 | (acc, [key, value]) => {
114 | const width = transformWidths(
115 | value === "custom" ? "6|6" : value,
116 | index
117 | );
118 | acc.add(width ? columnClass(width, key) : "");
119 | return acc;
120 | },
121 | new Set()
122 | );
123 | return React.cloneElement(child, {
124 | // @ts-ignore
125 | columnClasses: [...columnClasses].join(" "),
126 | ...child.props,
127 | });
128 | })
129 | : children
130 | );
131 | }
132 | export function NotSupported({ _atom = "" }) {
133 | return {`This builtin is not currently supported: ${_atom}`}
;
134 | }
135 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Dropdown.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { LinkProps } from "./Basic";
3 | declare type DropdownProps = React.PropsWithChildren<{
4 | tag?: keyof HTMLElementTagNameMap;
5 | className?: string;
6 | }>;
7 | declare type DropdownWrapperProps = DropdownProps & {
8 | children: React.ReactElement;
9 | };
10 | export declare function DropdownWrapper(
11 | props: DropdownWrapperProps
12 | ): JSX.Element;
13 | declare type DropdownToggleProps = DropdownProps;
14 | export declare function DropdownToggle({
15 | tag,
16 | className,
17 | ...props
18 | }: DropdownToggleProps): React.DOMElement<
19 | {
20 | "aria-haspopup": string;
21 | "aria-expanded": boolean;
22 | className: string;
23 | onClick: () => void;
24 | children?: React.ReactNode;
25 | },
26 | Element
27 | >;
28 | declare type DropdownListProps = DropdownProps & {
29 | children:
30 | | React.ReactElement
31 | | React.ReactElement[];
32 | };
33 | export declare function DropdownList({
34 | tag,
35 | className,
36 | ...props
37 | }: DropdownListProps): React.DOMElement<
38 | {
39 | className: string;
40 | children: React.ReactNode &
41 | (
42 | | React.ReactElement<
43 | DropdownLinkProps,
44 | string | React.JSXElementConstructor
45 | >
46 | | React.ReactElement<
47 | DropdownLinkProps,
48 | string | React.JSXElementConstructor
49 | >[]
50 | );
51 | },
52 | Element
53 | >;
54 | declare type DropdownLinkProps = DropdownProps & LinkProps;
55 | export declare function DropdownLink({
56 | className,
57 | ...props
58 | }: DropdownLinkProps): React.FunctionComponentElement<
59 | import("./Basic").ElementProps<"a"> & {
60 | options?:
61 | | {
62 | href: string;
63 | target?: "_self" | "_blank" | undefined;
64 | preload?: "none" | "prerender" | "prefetch" | undefined;
65 | }
66 | | undefined;
67 | className?: string | undefined;
68 | button?: boolean | undefined;
69 | }
70 | >;
71 | export {};
72 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Dropdown.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { useIXEvent } from "../interactions";
3 | import { cj, useClickOut } from "../utils";
4 | import { Link } from "./Basic";
5 | import { NavbarContext } from "./Navbar";
6 | const DropdownContext = React.createContext({
7 | root: undefined,
8 | isOpen: false,
9 | toggleOpen: () => {},
10 | });
11 | export function DropdownWrapper(props) {
12 | const root = React.useRef(null);
13 | const [isOpen, setIsOpen] = React.useState(false);
14 | const toggleOpen = () => setIsOpen((isOpen) => !isOpen);
15 | const closeDropdown = React.useCallback(() => setIsOpen(false), [setIsOpen]);
16 | useClickOut(root, closeDropdown);
17 | useIXEvent(root.current, isOpen);
18 | return (
19 |
20 |
21 |
22 | );
23 | }
24 | function Dropdown({ tag = "div", className = "", ...props }) {
25 | const { root } = React.useContext(DropdownContext);
26 | const { isOpen: isNavbarOpen } = React.useContext(NavbarContext);
27 | return React.createElement(tag, {
28 | ...props,
29 | ref: root,
30 | className: cj(
31 | className,
32 | "w-dropdown",
33 | isNavbarOpen && "w--nav-dropdown-open"
34 | ),
35 | });
36 | }
37 | export function DropdownToggle({ tag = "div", className = "", ...props }) {
38 | const { isOpen, toggleOpen } = React.useContext(DropdownContext);
39 | const { isOpen: isNavbarOpen } = React.useContext(NavbarContext);
40 | return React.createElement(tag, {
41 | ...props,
42 | "aria-haspopup": "menu",
43 | "aria-expanded": isOpen,
44 | className: cj(
45 | className,
46 | "w-dropdown-toggle",
47 | isNavbarOpen && "w--nav-dropdown-toggle-open"
48 | ),
49 | onClick: toggleOpen,
50 | });
51 | }
52 | export function DropdownList({ tag = "nav", className = "", ...props }) {
53 | const { isOpen } = React.useContext(DropdownContext);
54 | const { isOpen: isNavbarOpen } = React.useContext(NavbarContext);
55 | return React.createElement(tag, {
56 | ...props,
57 | className: cj(
58 | className,
59 | "w-dropdown-list",
60 | isOpen && "w--open",
61 | isNavbarOpen && "w--nav-dropdown-list-open"
62 | ),
63 | });
64 | }
65 | export function DropdownLink({ className = "", ...props }) {
66 | const { isOpen: isNavbarOpen } = React.useContext(NavbarContext);
67 | return React.createElement(Link, {
68 | ...props,
69 | className: cj(
70 | className,
71 | "w-dropdown-link",
72 | isNavbarOpen && "w--nav-link-open"
73 | ),
74 | });
75 | }
76 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Facebook.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | declare type FacebookProps = React.PropsWithChildren<{
3 | className?: string;
4 | layout?: string;
5 | width?: number;
6 | height?: number;
7 | url?: string;
8 | locale?: string;
9 | }>;
10 | export declare function Facebook({
11 | className,
12 | layout,
13 | width,
14 | height,
15 | url,
16 | locale,
17 | ...props
18 | }: FacebookProps): JSX.Element;
19 | export {};
20 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Facebook.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { isUrl } from "../utils";
3 | export function Facebook({
4 | className = "",
5 | layout = "standard",
6 | width = 250,
7 | height = 50,
8 | url = "https://facebook.com/webflow",
9 | locale = "en_US",
10 | ...props
11 | }) {
12 | if (!isUrl(url)) {
13 | url = "https://facebook.com/webflow";
14 | }
15 | if (!/^http/.test(url)) {
16 | url = "http://" + url;
17 | }
18 | const params = {
19 | href: url,
20 | layout: layout,
21 | locale: locale,
22 | action: "like",
23 | show_faces: "false",
24 | share: "false",
25 | };
26 | const queryParams = Object.keys(params).map(
27 | (key) => `${key}=${encodeURIComponent(params[key])}`
28 | );
29 | const frameSrc = `https://www.facebook.com/plugins/like.php?${queryParams.join(
30 | "&"
31 | )}`;
32 | return (
33 |
34 |
39 |
40 | );
41 | }
42 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Form.d.ts:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Props } from "./Basic";
3 | declare type FormState = "normal" | "success" | "error";
4 | declare type FormWrapperProps = Props<"div"> & {
5 | state?: FormState;
6 | children: React.ReactElement<
7 | typeof FormForm | typeof FormSuccessMessage | typeof FormErrorMessage
8 | >[];
9 | };
10 | declare global {
11 | interface Window {
12 | grecaptcha: any;
13 | }
14 | }
15 | export declare function FormWrapper({
16 | className,
17 | state: initialState,
18 | onSubmit,
19 | children,
20 | ...props
21 | }: FormWrapperProps): any;
22 | declare type FormSelect = {
23 | options: Array<{
24 | v: string;
25 | t: string;
26 | }>;
27 | };
28 | declare type FormProps = Props;
29 | export declare function FormForm(props: FormProps<"form">): any;
30 | export declare function FormBlockLabel(props: FormProps<"label">): any;
31 | declare type FormInputProps = React.InputHTMLAttributes & {
32 | inputType?: string;
33 | };
34 | export declare function FormTextInput({
35 | className,
36 | ...props
37 | }: FormInputProps): any;
38 | declare type FormTextAreaProps = React.InputHTMLAttributes;
39 | export declare function FormTextarea({
40 | className,
41 | ...props
42 | }: FormTextAreaProps): any;
43 | export declare function FormInlineLabel({
44 | className,
45 | ...props
46 | }: FormProps<"span">): any;
47 | export declare function FormCheckboxWrapper({
48 | className,
49 | ...props
50 | }: FormProps<"label">): any;
51 | export declare function FormRadioWrapper({
52 | className,
53 | ...props
54 | }: FormProps<"label">): any;
55 | export declare function FormCheckboxInput({
56 | className,
57 | checked,
58 | ...props
59 | }: FormInputProps): any;
60 | export declare function FormRadioInput({
61 | className,
62 | inputType,
63 | ...props
64 | }: FormInputProps): any;
65 | declare type FileUploadWrapperProps = Props<"div"> & {
66 | maxSize?: number;
67 | };
68 | export declare function FormFileUploadWrapper({
69 | maxSize,
70 | ...props
71 | }: FileUploadWrapperProps): any;
72 | export declare function _FormFileUploadWrapper({
73 | className,
74 | ...props
75 | }: FileUploadWrapperProps): any;
76 | export declare function FormFileUploadDefault({
77 | className,
78 | ...props
79 | }: FormProps<"div">): any;
80 | export declare function FormFileUploadInput({
81 | className,
82 | ...props
83 | }: FormInputProps): any;
84 | export declare function FormFileUploadLabel({
85 | className,
86 | ...props
87 | }: FormProps<"label">): any;
88 | export declare function FormFileUploadText({
89 | className,
90 | ...props
91 | }: FormProps<"div">): any;
92 | export declare function FormFileUploadInfo({
93 | className,
94 | ...props
95 | }: FormProps<"div">): any;
96 | export declare function FormFileUploadUploading({
97 | className,
98 | ...props
99 | }: FormProps<"div">): any;
100 | export declare function FormFileUploadUploadingBtn({
101 | className,
102 | ...props
103 | }: FormProps<"div">): any;
104 | export declare function FormFileUploadUploadingIcon({
105 | className,
106 | ...props
107 | }: FormProps<"div">): any;
108 | export declare function FormFileUploadSuccess({
109 | className,
110 | ...props
111 | }: FormProps<"div">): any;
112 | export declare function FormFileUploadFile({
113 | className,
114 | ...props
115 | }: FormProps<"div">): any;
116 | export declare function FormFileUploadFileName({
117 | className,
118 | ...props
119 | }: FormProps<"div">): any;
120 | export declare function FormFileUploadRemoveLink({
121 | className,
122 | ...props
123 | }: FormProps<"div">): any;
124 | export declare function FormFileUploadError({
125 | className,
126 | ...props
127 | }: FormProps<"div">): any;
128 | declare type FormErrorMessageProps = HTMLDivElement & {
129 | errors: {
130 | SIZE_ERROR: string;
131 | TYPE_ERROR: string;
132 | GENERIC_ERROR: string;
133 | };
134 | };
135 | export declare function FormFileUploadErrorMsg({
136 | errors,
137 | className,
138 | ...props
139 | }: FormErrorMessageProps): any;
140 | export declare function FormButton({
141 | className,
142 | value,
143 | ...props
144 | }: FormInputProps): any;
145 | export declare function SearchForm(props: FormProps<"form">): any;
146 | export declare function SearchInput({
147 | className,
148 | ...props
149 | }: FormInputProps): any;
150 | export declare function SearchButton({
151 | value,
152 | className,
153 | ...props
154 | }: FormInputProps): any;
155 | export declare function FormSuccessMessage({
156 | className,
157 | ...props
158 | }: Props<"div">): any;
159 | export declare function FormErrorMessage({
160 | className,
161 | ...props
162 | }: Props<"div">): any;
163 | export declare function FormSelect({
164 | options,
165 | className,
166 | ...props
167 | }: FormProps<"select"> & FormSelect): any;
168 | declare type FormReCaptchaProps = {
169 | siteKey: string;
170 | theme?: "light" | "dark";
171 | size?: "compact" | "normal" | "invisible";
172 | };
173 | export declare function FormReCaptcha({
174 | siteKey,
175 | theme,
176 | size,
177 | }: FormReCaptchaProps): JSX.Element;
178 | export {};
179 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Map.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | declare global {
3 | interface Window {
4 | google: {
5 | maps: any;
6 | };
7 | }
8 | }
9 | declare type MapWidgetProps = {
10 | className?: string;
11 | apiKey: string;
12 | zoom?: number;
13 | latlng?: string;
14 | mapStyle?: "roadmap" | "satellite" | "hybrid" | "terrain";
15 | tooltip?: string;
16 | title?: string;
17 | enableScroll?: boolean;
18 | enableTouch?: boolean;
19 | };
20 | export declare function MapWidget({
21 | apiKey,
22 | mapStyle,
23 | zoom,
24 | latlng,
25 | tooltip,
26 | title,
27 | enableScroll,
28 | enableTouch,
29 | className,
30 | ...props
31 | }: MapWidgetProps): JSX.Element;
32 | export default Map;
33 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Map.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from "react";
2 | import { cj, loadScript } from "../utils";
3 | function buildTitle(title, tooltip) {
4 | let markerTitle = "Map pin";
5 | if (title && tooltip) {
6 | markerTitle = `Map pin on ${title} showing location of ${tooltip}`;
7 | } else if (title && !tooltip) {
8 | markerTitle = `Map pin on ${title}`;
9 | } else if (!title && tooltip) {
10 | markerTitle = `Map pin showing location of ${tooltip}`;
11 | }
12 | return markerTitle;
13 | }
14 | export function MapWidget({
15 | apiKey = "",
16 | mapStyle = "roadmap",
17 | zoom = 12,
18 | latlng = "51.511214,-0.119824",
19 | tooltip = "",
20 | title = "",
21 | enableScroll = true,
22 | enableTouch = true,
23 | className = "",
24 | ...props
25 | }) {
26 | const mapRef = useRef(null);
27 | useEffect(() => {
28 | const loadMap = () => {
29 | if (!mapRef.current) return;
30 | if (!window?.google?.maps) return;
31 | const { Map, Marker, InfoWindow } = window.google.maps;
32 | const coords = latlng.split(",");
33 | const center = { lat: parseFloat(coords[0]), lng: parseFloat(coords[1]) };
34 | const map = new Map(mapRef.current, {
35 | zoom,
36 | center,
37 | mapTypeId: mapStyle,
38 | mapTypeControl: false,
39 | panControl: false,
40 | streetViewControl: false,
41 | draggable: enableTouch,
42 | scrollwheel: enableScroll,
43 | zoomControl: true,
44 | });
45 | const marker = new Marker({
46 | draggable: false,
47 | position: center,
48 | title: buildTitle(title, tooltip),
49 | map,
50 | });
51 | if (tooltip) {
52 | new InfoWindow({
53 | disableAutoPan: true,
54 | content: tooltip,
55 | position: center,
56 | }).open({ anchor: marker, map });
57 | }
58 | window.google.maps.event.addListener(marker, "click", function () {
59 | window.open(`https://maps.google.com/?z=${zoom}&daddr=${latlng}`);
60 | });
61 | };
62 | loadScript(
63 | `https://maps.googleapis.com/maps/api/js?v=3.52.5&key=${apiKey}`,
64 | {
65 | cacheRegex: /maps\.googleapis\.com\/maps\/api\/js\?v=3\.52\.5\&key=/gi,
66 | }
67 | ).then(loadMap);
68 | }, [
69 | apiKey,
70 | mapStyle,
71 | zoom,
72 | latlng,
73 | tooltip,
74 | title,
75 | enableScroll,
76 | enableTouch,
77 | mapRef,
78 | ]);
79 | return (
80 |
86 | );
87 | }
88 | export default Map;
89 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Navbar.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { EASING_FUNCTIONS } from "../utils";
3 | import { LinkProps, ContainerProps } from "./Basic";
4 | declare const BREAKPOINTS: {
5 | medium: number;
6 | small: number;
7 | tiny: number;
8 | };
9 | declare type NavbarConfig = {
10 | animation: string;
11 | collapse: keyof typeof BREAKPOINTS;
12 | docHeight: boolean;
13 | duration: number;
14 | easing: keyof typeof EASING_FUNCTIONS;
15 | easing2: keyof typeof EASING_FUNCTIONS;
16 | noScroll: boolean;
17 | };
18 | export declare const NavbarContext: React.Context<
19 | NavbarConfig & {
20 | animDirect: -1 | 1;
21 | animOver: boolean;
22 | getBodyHeight: () => number | void;
23 | getOverlayHeight: () => number | void;
24 | isOpen: boolean;
25 | menu: React.MutableRefObject;
26 | root: React.MutableRefObject;
27 | toggleOpen: () => void;
28 | navbarMounted: boolean;
29 | }
30 | >;
31 | declare type NavbarChildrenType =
32 | | NavbarContainerProps
33 | | NavbarBrandProps
34 | | NavbarMenuProps
35 | | NavbarButtonProps;
36 | declare type NavbarProps = {
37 | tag: React.ElementType;
38 | config: NavbarConfig;
39 | className?: string;
40 | children?:
41 | | React.ReactElement[]
42 | | React.ReactElement;
43 | };
44 | export declare function NavbarWrapper(props: NavbarProps): JSX.Element;
45 | declare type NavbarContainerProps = ContainerProps & {
46 | toggleOpen: () => void;
47 | isOpen: boolean;
48 | children: React.ReactNode;
49 | };
50 | export declare function NavbarContainer({
51 | children,
52 | ...props
53 | }: NavbarContainerProps): JSX.Element;
54 | declare type NavbarBrandProps = LinkProps;
55 | export declare function NavbarBrand({
56 | className,
57 | ...props
58 | }: NavbarBrandProps): JSX.Element;
59 | declare type NavbarMenuProps = React.PropsWithChildren<{
60 | tag?: React.ElementType;
61 | className?: string;
62 | isOpen?: boolean;
63 | }>;
64 | export declare function NavbarMenu({
65 | tag,
66 | className,
67 | ...props
68 | }: NavbarMenuProps): React.ReactElement<
69 | any,
70 | string | React.JSXElementConstructor
71 | >;
72 | declare type NavbarLinkProps = LinkProps;
73 | export declare function NavbarLink({
74 | className,
75 | ...props
76 | }: NavbarLinkProps): JSX.Element;
77 | declare type NavbarButtonProps = React.PropsWithChildren<{
78 | tag?: React.ElementType;
79 | className?: string;
80 | }>;
81 | export declare function NavbarButton({
82 | tag,
83 | className,
84 | ...props
85 | }: NavbarButtonProps): React.ReactElement<
86 | any,
87 | string | React.JSXElementConstructor
88 | >;
89 | export {};
90 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Navbar.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | EASING_FUNCTIONS,
4 | cj,
5 | debounce,
6 | extractElement,
7 | isServer,
8 | useLayoutEffect,
9 | useResizeObserver,
10 | } from "../utils";
11 | import { Link, Container } from "./Basic";
12 | const BREAKPOINTS = {
13 | medium: 991,
14 | small: 767,
15 | tiny: 479,
16 | };
17 | export const NavbarContext = React.createContext({
18 | animDirect: 1,
19 | animOver: false,
20 | animation: "animation",
21 | collapse: "medium",
22 | docHeight: false,
23 | duration: 400,
24 | easing2: "ease",
25 | easing: "ease",
26 | getBodyHeight: () => {},
27 | getOverlayHeight: () => {},
28 | isOpen: false,
29 | noScroll: false,
30 | toggleOpen: () => {},
31 | navbarMounted: false,
32 | menu: undefined,
33 | root: undefined,
34 | });
35 | function getAnimationKeyframes({ axis = "Y", start, end }) {
36 | const t = `translate${axis}`;
37 | return [{ transform: `${t}(${start}px)` }, { transform: `${t}(${end}px)` }];
38 | }
39 | export function NavbarWrapper(props) {
40 | const { animation, docHeight, easing, easing2, duration, noScroll } =
41 | props.config;
42 | const root = React.useRef(null);
43 | const menu = React.useRef(null);
44 | const animOver = /^over/.test(animation);
45 | const animDirect = /left$/.test(animation) ? -1 : 1;
46 | const getBodyHeight = React.useCallback(() => {
47 | if (isServer) return;
48 | return docHeight
49 | ? document.documentElement.scrollHeight
50 | : document.body.scrollHeight;
51 | }, [docHeight]);
52 | const getOverlayHeight = React.useCallback(() => {
53 | if (isServer || !root.current) return;
54 | let height = getBodyHeight();
55 | if (!height) return;
56 | const style = getComputedStyle(root.current);
57 | if (!animOver && style.position !== "fixed") {
58 | height -= root.current.offsetHeight;
59 | }
60 | return height;
61 | }, [animOver, getBodyHeight]);
62 | const getOffsetHeight = React.useCallback(() => {
63 | if (!root.current || !menu.current) return 0;
64 | return root.current.offsetHeight + menu.current.offsetHeight;
65 | }, []);
66 | const [isOpen, setIsOpen] = React.useState(false);
67 | const toggleOpen = debounce(() => {
68 | if (!menu.current) return;
69 | // menu is open and should be closed
70 | if (isOpen) {
71 | const keyframes = animOver
72 | ? getAnimationKeyframes({
73 | axis: "X",
74 | start: 0,
75 | end: animDirect * menu.current.offsetWidth,
76 | })
77 | : getAnimationKeyframes({ start: 0, end: -getOffsetHeight() });
78 | const anim = menu.current.animate(keyframes, {
79 | easing: EASING_FUNCTIONS[easing2] ?? "ease",
80 | duration,
81 | fill: "forwards",
82 | });
83 | anim.onfinish = () => {
84 | setIsOpen(!isOpen);
85 | };
86 | return;
87 | }
88 | setIsOpen(!isOpen);
89 | });
90 | useLayoutEffect(() => {
91 | if (!menu.current) return;
92 | // menu is closed and will open, but the animation only runs when isOpen is true
93 | if (isOpen) {
94 | const keyframes = animOver
95 | ? getAnimationKeyframes({
96 | axis: "X",
97 | start: animDirect * menu.current.offsetWidth,
98 | end: 0,
99 | })
100 | : getAnimationKeyframes({ start: -getOffsetHeight(), end: 0 });
101 | menu.current.animate(keyframes, {
102 | easing: EASING_FUNCTIONS[easing] ?? "ease",
103 | duration,
104 | fill: "forwards",
105 | });
106 | }
107 | }, [
108 | animDirect,
109 | animOver,
110 | duration,
111 | easing,
112 | getBodyHeight,
113 | getOffsetHeight,
114 | isOpen,
115 | ]);
116 | // if the menu is opened and noScroll === false prevent scrolling
117 | useLayoutEffect(() => {
118 | if (isOpen && noScroll) {
119 | document.body.style.overflowY = "hidden";
120 | } else {
121 | document.body.style.overflowY = "";
122 | }
123 | return () => {
124 | document.body.style.overflowY = "";
125 | };
126 | }, [isOpen, noScroll]);
127 | // Closes menu when the window is resized
128 | const closeOnResize = React.useCallback(() => setIsOpen(false), [setIsOpen]);
129 | useResizeObserver(root, closeOnResize);
130 | return (
131 |
145 |
146 |
147 | );
148 | }
149 | /**
150 | * Navbar menu gets appended to the overlay when it's open.
151 | * This function extracts the child menu when that's the case.
152 | * */
153 | const maybeExtractChildMenu = (children, isOpen) => {
154 | if (!isOpen) return { childMenu: null, rest: children };
155 | const { extracted, tree } = extractElement(
156 | React.Children.toArray(children),
157 | NavbarMenu
158 | );
159 | return { childMenu: extracted, rest: tree };
160 | };
161 | function Navbar({ tag = "div", className = "", children, config, ...props }) {
162 | const { root, collapse } = React.useContext(NavbarContext);
163 | const [shouldExtractMenu, setShouldExtractMenu] = React.useState(true);
164 | const extractMenuCallback = React.useCallback(
165 | (entry) =>
166 | setShouldExtractMenu(entry.contentRect.width <= BREAKPOINTS[collapse]),
167 | [setShouldExtractMenu]
168 | );
169 | const bodyRef = React.useRef(
170 | typeof document !== "undefined" ? document.body : null
171 | );
172 | useResizeObserver(bodyRef, extractMenuCallback);
173 | const { childMenu, rest } = React.useMemo(
174 | () => maybeExtractChildMenu(children, shouldExtractMenu),
175 | [children, shouldExtractMenu]
176 | );
177 | return React.createElement(
178 | tag,
179 | {
180 | ...props,
181 | className: cj(className, "w-nav"),
182 | "data-collapse": config.collapse,
183 | "data-animation": config.animation,
184 | ref: root,
185 | },
186 | <>
187 | {rest}
188 | {childMenu}
189 | >
190 | );
191 | }
192 | function NavbarOverlay({ children }) {
193 | const { isOpen, getOverlayHeight, toggleOpen } =
194 | React.useContext(NavbarContext);
195 | const overlayToggleOpen = React.useCallback(
196 | (e) => {
197 | // prevent link clicks to close the overlay
198 | if (e.target === e.currentTarget) {
199 | toggleOpen();
200 | }
201 | },
202 | [toggleOpen]
203 | );
204 | return (
205 |
215 | {children}
216 |
217 | );
218 | }
219 | export function NavbarContainer({ children, ...props }) {
220 | const ref = React.useRef(null);
221 | const { isOpen } = React.useContext(NavbarContext);
222 | const updateLinkStyles = React.useCallback(
223 | (entry) => {
224 | const { maxWidth: containerMaxWidth } = getComputedStyle(entry.target);
225 | document
226 | .querySelectorAll(".w-nav-menu>.w-dropdown,.w-nav-menu>.w-nav-link")
227 | .forEach((node) => {
228 | if (!(node instanceof HTMLElement)) return;
229 | if (!isOpen) {
230 | node.style.maxWidth = "";
231 | return;
232 | }
233 | const { maxWidth } = getComputedStyle(node);
234 | node.style.maxWidth =
235 | !maxWidth || maxWidth === "none" ? containerMaxWidth : "";
236 | });
237 | },
238 | [isOpen]
239 | );
240 | useResizeObserver(ref, updateLinkStyles);
241 | return (
242 |
243 | {children}
244 |
245 | );
246 | }
247 | export function NavbarBrand({ className = "", ...props }) {
248 | return ;
249 | }
250 | export function NavbarMenu({ tag = "nav", className = "", ...props }) {
251 | const { getBodyHeight, animOver, isOpen, menu } =
252 | React.useContext(NavbarContext);
253 | return React.createElement(tag, {
254 | ...props,
255 | className: cj(className, "w-nav-menu"),
256 | ...(isOpen ? { "data-nav-menu-open": "" } : {}),
257 | style: animOver ? { height: getBodyHeight() } : {},
258 | ref: menu,
259 | });
260 | }
261 | export function NavbarLink({ className = "", ...props }) {
262 | const { isOpen } = React.useContext(NavbarContext);
263 | return (
264 |
268 | );
269 | }
270 | export function NavbarButton({ tag = "div", className = "", ...props }) {
271 | const { isOpen, toggleOpen } = React.useContext(NavbarContext);
272 | return React.createElement(tag, {
273 | ...props,
274 | "aria-label": "menu",
275 | "aria-expanded": isOpen ? "true" : "false",
276 | "aria-haspopup": "menu",
277 | "aria-controls": "w-nav-overlay",
278 | role: "button",
279 | tabIndex: 0,
280 | className: cj(className, "w-nav-button", isOpen && "w--open"),
281 | onClick: toggleOpen,
282 | });
283 | }
284 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Slider.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { EASING_FUNCTIONS } from "../utils";
3 | declare type SliderConfig = {
4 | navSpacing: number;
5 | navShadow: boolean;
6 | autoplay: boolean;
7 | delay: number;
8 | iconArrows: boolean;
9 | animation: "slide" | "cross" | "outin" | "fade" | "over";
10 | navNumbers: boolean;
11 | easing: keyof typeof EASING_FUNCTIONS;
12 | navRound: boolean;
13 | hideArrows: boolean;
14 | disableSwipe: boolean;
15 | duration: number;
16 | infinite: boolean;
17 | autoMax: number;
18 | navInvert: boolean;
19 | };
20 | declare type SlideState = {
21 | current: number;
22 | previous: number;
23 | };
24 | export declare const SliderContext: React.Context<
25 | SliderConfig & {
26 | slideAmount: number;
27 | setSlideAmount: React.Dispatch>;
28 | slide: SlideState;
29 | setCurrentSlide: (current: number) => void;
30 | goToNextSlide: () => void;
31 | goToPreviousSlide: () => void;
32 | isAutoplayPaused: boolean;
33 | setAutoplayPause: React.Dispatch>;
34 | }
35 | >;
36 | declare type SliderChildrenType =
37 | | SliderSlideProps
38 | | SliderArrowProps
39 | | SliderNavProps
40 | | SliderMaskProps;
41 | declare type SliderWrapperProps = SliderConfig & {
42 | className?: string;
43 | children?:
44 | | React.ReactElement[]
45 | | React.ReactElement;
46 | };
47 | export declare function SliderWrapper({
48 | className,
49 | ...props
50 | }: SliderWrapperProps): JSX.Element;
51 | declare type SliderMaskProps = React.PropsWithChildren<{
52 | className?: string;
53 | }>;
54 | export declare function SliderMask({
55 | className,
56 | children,
57 | ...props
58 | }: SliderMaskProps): JSX.Element;
59 | declare type SliderSlideProps = React.PropsWithChildren<{
60 | style?: React.CSSProperties;
61 | tag?: string;
62 | className?: string;
63 | index: number;
64 | }>;
65 | export declare function SliderSlide({
66 | tag,
67 | className,
68 | style,
69 | index,
70 | ...props
71 | }: SliderSlideProps): React.DOMElement<
72 | {
73 | className: string;
74 | style: {};
75 | "aria-label": string;
76 | role: string;
77 | ref: (node: any) => void;
78 | "aria-hidden": string;
79 | children?: React.ReactNode;
80 | },
81 | any
82 | >;
83 | declare type SliderArrowProps = React.PropsWithChildren<{
84 | className?: string;
85 | dir: "left" | "right";
86 | }>;
87 | export declare function SliderArrow({
88 | className,
89 | dir,
90 | children,
91 | ...props
92 | }: SliderArrowProps): JSX.Element;
93 | declare type SliderNavProps = {
94 | className?: string;
95 | };
96 | export declare function SliderNav({
97 | className,
98 | ...props
99 | }: SliderNavProps): JSX.Element;
100 | export {};
101 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Tabs.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { EASING_FUNCTIONS } from "../utils";
3 | import { Props } from "./Basic";
4 | declare type TabsWrapperProps = Props<
5 | "div",
6 | {
7 | current: string;
8 | easing: keyof typeof EASING_FUNCTIONS;
9 | fadeIn: number;
10 | fadeOut: number;
11 | children?:
12 | | React.ReactElement[]
13 | | React.ReactElement;
14 | }
15 | >;
16 | export declare function TabsWrapper({
17 | className,
18 | fadeIn,
19 | fadeOut,
20 | easing,
21 | current: initialCurrent,
22 | ...props
23 | }: TabsWrapperProps): JSX.Element;
24 | declare type TabsMenuProps = {
25 | tag?: React.ElementType;
26 | className?: string;
27 | children?: React.ReactElement[];
28 | };
29 | export declare function TabsMenu({
30 | tag,
31 | className,
32 | ...props
33 | }: TabsMenuProps): React.ReactElement<
34 | any,
35 | string | React.JSXElementConstructor
36 | >;
37 | declare type TabsLinkProps = Props<
38 | "a",
39 | {
40 | "data-w-tab": string;
41 | }
42 | >;
43 | export declare function TabsLink({
44 | className,
45 | ...props
46 | }: TabsLinkProps): JSX.Element;
47 | declare type TabsContentProps = {
48 | tag?: React.ElementType;
49 | className?: string;
50 | children?:
51 | | React.ReactElement[]
52 | | React.ReactElement;
53 | };
54 | export declare function TabsContent({
55 | tag,
56 | className,
57 | ...props
58 | }: TabsContentProps): React.ReactElement<
59 | any,
60 | string | React.JSXElementConstructor
61 | >;
62 | declare type TabsPaneProps = React.PropsWithChildren<{
63 | tag?: React.ElementType;
64 | className?: string;
65 | "data-w-tab": string;
66 | }>;
67 | export declare function TabsPane({
68 | tag,
69 | className,
70 | ...props
71 | }: TabsPaneProps): React.ReactElement<
72 | any,
73 | string | React.JSXElementConstructor
74 | >;
75 | export {};
76 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Tabs.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { triggerIXEvent } from "../interactions";
3 | import { cj, debounce, EASING_FUNCTIONS, useLayoutEffect } from "../utils";
4 | const tabsContext = React.createContext({
5 | current: "",
6 | onTabClick: () => {},
7 | onLinkKeyDown: () => {},
8 | });
9 | export function TabsWrapper({
10 | className = "",
11 | fadeIn,
12 | fadeOut,
13 | easing,
14 | current: initialCurrent,
15 | ...props
16 | }) {
17 | const [current, setCurrent] = React.useState("");
18 | const changeTab = React.useCallback(
19 | (next) => {
20 | const currentTab = document.querySelector(
21 | `.w-tab-pane[data-w-tab="${current}"]`
22 | );
23 | const nextTab = document.querySelector(
24 | `.w-tab-pane[data-w-tab="${next}"]`
25 | );
26 | const easingFn = EASING_FUNCTIONS[easing] ?? "ease";
27 | const animation = currentTab?.animate([{ opacity: 1 }, { opacity: 0 }], {
28 | duration: fadeOut,
29 | fill: "forwards",
30 | easing: easingFn,
31 | });
32 | if (animation) {
33 | animation.onfinish = () => {
34 | setCurrent(next);
35 | nextTab?.animate([{ opacity: 0 }, { opacity: 1 }], {
36 | duration: fadeIn,
37 | fill: "forwards",
38 | easing: easingFn,
39 | });
40 | };
41 | } else {
42 | setCurrent(next);
43 | }
44 | },
45 | [current, easing, fadeIn, fadeOut]
46 | );
47 | // Trigger first tab change manually
48 | const firstRender = React.useRef(true);
49 | useLayoutEffect(() => {
50 | if (!firstRender.current) return;
51 | firstRender.current = false;
52 | setTimeout(() => {
53 | changeTab(initialCurrent);
54 | }, 1);
55 | }, [changeTab, initialCurrent]);
56 | const onTabClick = debounce(changeTab);
57 | const onLinkKeyDown = debounce((event) => {
58 | event.preventDefault();
59 | const currentTab = document.querySelector(
60 | `.w-tab-pane[data-w-tab="${current}"]`
61 | );
62 | const allTabs = document.querySelectorAll(".w-tab-pane");
63 | const firstTab = allTabs[0];
64 | const lastTab = allTabs[allTabs.length - 1];
65 | const nextTab = (() => {
66 | switch (event.key) {
67 | case "ArrowUp":
68 | case "ArrowLeft":
69 | return currentTab.previousElementSibling ?? lastTab;
70 | case "ArrowDown":
71 | case "ArrowRight":
72 | return currentTab.nextElementSibling ?? firstTab;
73 | case "Home":
74 | return firstTab;
75 | case "End":
76 | return lastTab;
77 | }
78 | })();
79 | if (nextTab) changeTab(nextTab.getAttribute("data-w-tab"));
80 | });
81 | return (
82 |
83 |
84 |
85 | );
86 | }
87 | export function TabsMenu({ tag = "div", className = "", ...props }) {
88 | return React.createElement(tag, {
89 | ...props,
90 | className: cj(className, "w-tab-menu"),
91 | role: "tablist",
92 | });
93 | }
94 | export function TabsLink({ className = "", ...props }) {
95 | const { current, onTabClick, onLinkKeyDown } = React.useContext(tabsContext);
96 | const isCurrent = current === props["data-w-tab"];
97 | const ref = React.useCallback(
98 | (node) => {
99 | if (!node) return;
100 | triggerIXEvent(node, isCurrent);
101 | },
102 | [isCurrent]
103 | );
104 | return (
105 | onTabClick(props["data-w-tab"])}
114 | onKeyDown={onLinkKeyDown}
115 | role="tab"
116 | tabIndex={isCurrent ? 0 : -1}
117 | aria-selected={isCurrent}
118 | aria-controls={props["data-w-tab"]}
119 | />
120 | );
121 | }
122 | export function TabsContent({ tag = "div", className = "", ...props }) {
123 | return React.createElement(tag, {
124 | ...props,
125 | className: cj(className, "w-tab-content"),
126 | });
127 | }
128 | export function TabsPane({ tag = "div", className = "", ...props }) {
129 | const { current } = React.useContext(tabsContext);
130 | const isCurrent = current === props["data-w-tab"];
131 | return React.createElement(tag, {
132 | ...props,
133 | className: cj(className, "w-tab-pane", isCurrent && "w--tab-active"),
134 | role: "tabpanel",
135 | "aria-labelledby": props["data-w-tab"],
136 | });
137 | }
138 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Twitter.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | declare type TwitterSize = "m" | "l";
3 | declare type TwitterMode = "follow" | "tweet";
4 | declare type TwitterProps = React.PropsWithChildren<{
5 | className?: string;
6 | mode?: TwitterMode;
7 | url?: string;
8 | text?: string;
9 | size?: TwitterSize;
10 | }>;
11 | declare global {
12 | interface Window {
13 | twttr: any;
14 | }
15 | }
16 | export declare function Twitter({
17 | className,
18 | url,
19 | mode,
20 | size,
21 | text,
22 | ...props
23 | }: TwitterProps): JSX.Element;
24 | export {};
25 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Twitter.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { isUrl, loadScript } from "../utils";
3 | const modeDict = {
4 | follow: "createFollowButton",
5 | tweet: "createShareButton",
6 | };
7 | const sizeDict = {
8 | m: "medium",
9 | l: "large",
10 | };
11 | export function Twitter({
12 | className = "",
13 | url = "https://webflow.com",
14 | mode = "tweet",
15 | size = "m",
16 | text = "Check out this site",
17 | ...props
18 | }) {
19 | const ref = React.useRef(null);
20 | if (!isUrl(url)) {
21 | if (mode === "tweet") {
22 | url = "https://webflow.com/";
23 | } else if (mode === "follow") {
24 | url = "webflow";
25 | }
26 | }
27 | React.useEffect(() => {
28 | let isComponentMounted = true;
29 | loadScript("https://platform.twitter.com/widgets.js").then(() => {
30 | if (isComponentMounted) {
31 | if (window.twttr) {
32 | const twitterButtonOption = window.twttr.widgets[modeDict[mode]];
33 | if (twitterButtonOption) {
34 | twitterButtonOption(url, ref?.current, {
35 | size: sizeDict[size],
36 | text,
37 | });
38 | }
39 | }
40 | }
41 | });
42 | return () => {
43 | isComponentMounted = false;
44 | };
45 | }, []);
46 | return (
47 |
52 | );
53 | }
54 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Typography.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | export function Heading({ tag = "h1", ...props }) {
3 | return React.createElement(tag, props);
4 | }
5 | export function Paragraph(props) {
6 | return React.createElement("p", props);
7 | }
8 | export function Emphasized(props) {
9 | return ;
10 | }
11 | export function Strong(props) {
12 | return React.createElement("strong", props);
13 | }
14 | export function Figure({ className = "", figure, ...props }) {
15 | const { type, align } = figure;
16 | if (align) {
17 | className += `w-richtext-align-${align} `;
18 | }
19 | if (type) {
20 | className += `w-richtext-align-${type} `;
21 | }
22 | return ;
23 | }
24 | export function Figcaption(props) {
25 | return ;
26 | }
27 | export function Subscript(props) {
28 | return ;
29 | }
30 | export function Superscript(props) {
31 | return ;
32 | }
33 | export function RichText({ tag = "div", className = "", ...props }) {
34 | return React.createElement(tag, {
35 | className: className + " w-richtext",
36 | ...props,
37 | });
38 | }
39 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Video.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { Embed } from "../types";
3 | declare type VideoProps = {
4 | className?: string;
5 | options: Embed.Video;
6 | };
7 | export declare function Video({
8 | className,
9 | options,
10 | ...props
11 | }: VideoProps): JSX.Element;
12 | export {};
13 |
--------------------------------------------------------------------------------
/devlink/_Builtin/Video.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { cj } from "../utils";
3 | const getAspectRatio = ({ width, height }) =>
4 | height && width ? height / width : 0;
5 | export function Video({
6 | className = "",
7 | options = { height: 0, width: 0, title: "", url: "" },
8 | ...props
9 | }) {
10 | const { height, title, url, width } = options;
11 | return (
12 |
17 |
27 |
28 | );
29 | }
30 |
--------------------------------------------------------------------------------
/devlink/_Builtin/YouTubeVideo.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | declare type YouTubeVideoProps = {
3 | className?: string;
4 | title: string;
5 | videoId: string;
6 | aspectRatio?: number;
7 | startAt?: number;
8 | showAllRelatedVideos?: boolean;
9 | controls?: boolean;
10 | autoplay?: boolean;
11 | muted?: boolean;
12 | privacyMode?: boolean;
13 | };
14 | export declare function YouTubeVideo({
15 | className,
16 | title,
17 | videoId,
18 | aspectRatio,
19 | startAt,
20 | showAllRelatedVideos,
21 | controls,
22 | autoplay,
23 | muted,
24 | privacyMode,
25 | ...props
26 | }: YouTubeVideoProps): JSX.Element;
27 | export {};
28 |
--------------------------------------------------------------------------------
/devlink/_Builtin/YouTubeVideo.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { cj } from "../utils";
3 | const DEFAULT_16_9_RATIO = 0.5617021276595745;
4 | export function YouTubeVideo({
5 | className = "",
6 | title,
7 | videoId,
8 | aspectRatio = DEFAULT_16_9_RATIO,
9 | startAt = 0,
10 | showAllRelatedVideos = false,
11 | controls = true,
12 | autoplay = false,
13 | muted = false,
14 | privacyMode = false,
15 | ...props
16 | }) {
17 | const baseUrl = privacyMode
18 | ? "https://www.youtube-nocookie.com/embed"
19 | : "https://www.youtube.com/embed";
20 | const urlParams = Object.entries({
21 | startAt,
22 | showAllRelatedVideos,
23 | controls,
24 | autoplay,
25 | muted,
26 | })
27 | .map(([k, v]) => `${k}=${Number(v)}`)
28 | .join("&");
29 | const iframeStyle = {
30 | position: "absolute",
31 | left: 0,
32 | top: 0,
33 | width: "100%",
34 | height: "100%",
35 | pointerEvents: "auto",
36 | };
37 | return (
38 |
43 |
52 |
53 | );
54 | }
55 |
--------------------------------------------------------------------------------
/devlink/_Builtin/index.d.ts:
--------------------------------------------------------------------------------
1 | export * from "./Basic";
2 | export * from "./Tabs";
3 | export * from "./Dropdown";
4 | export * from "./Navbar";
5 | export * from "./Facebook";
6 | export * from "./Typography";
7 | export * from "./Twitter";
8 | export * from "./Form";
9 | export * from "./Map";
10 | export * from "./Slider";
11 | export * from "./Video";
12 | export * from "./YouTubeVideo";
13 | export * from "./BackgroundVideo";
14 |
--------------------------------------------------------------------------------
/devlink/_Builtin/index.js:
--------------------------------------------------------------------------------
1 | export * from "./Basic";
2 | export * from "./Tabs";
3 | export * from "./Dropdown";
4 | export * from "./Navbar";
5 | export * from "./Facebook";
6 | export * from "./Typography";
7 | export * from "./Twitter";
8 | export * from "./Form";
9 | export * from "./Map";
10 | export * from "./Slider";
11 | export * from "./Video";
12 | export * from "./YouTubeVideo";
13 | export * from "./BackgroundVideo";
14 |
--------------------------------------------------------------------------------
/devlink/devlink.d.ts:
--------------------------------------------------------------------------------
1 | type IXData = any;
2 |
3 | type IXEngine = {
4 | init: (data: IXData) => void;
5 | };
6 |
7 | export const createIX2Engine: () => IXEngine;
8 |
--------------------------------------------------------------------------------
/devlink/index.d.ts:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | export * as _Builtin from "./_Builtin";
4 | export * from "./interactions";
5 | export * from "./utils";
6 | export * from "./devlink";
7 | export * from "./AboutHero";
8 | export * from "./AboutInformation";
9 | export * from "./Brands";
10 | export * from "./Cta";
11 | export * from "./Details";
12 | export * from "./Features";
13 | export * from "./Footer";
14 | export * from "./Hero";
15 | export * from "./JobListing";
16 | export * from "./Navbar";
17 | export * from "./Newnav";
18 | export * from "./PageHeading";
19 | export * from "./Pricing";
20 | export * from "./PricingGrid";
21 | export * from "./Stats";
22 |
--------------------------------------------------------------------------------
/devlink/index.js:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | export * as _Builtin from "./_Builtin";
4 | export * from "./interactions";
5 | export * from "./utils";
6 | export * from "./devlink";
7 | export * from "./AboutHero";
8 | export * from "./AboutInformation";
9 | export * from "./Brands";
10 | export * from "./Cta";
11 | export * from "./Details";
12 | export * from "./Features";
13 | export * from "./Footer";
14 | export * from "./Hero";
15 | export * from "./JobListing";
16 | export * from "./Navbar";
17 | export * from "./Newnav";
18 | export * from "./PageHeading";
19 | export * from "./Pricing";
20 | export * from "./PricingGrid";
21 | export * from "./Stats";
22 |
--------------------------------------------------------------------------------
/devlink/interactions.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { CSSModules } from "./types";
3 | declare type IXData = any;
4 | declare type IXEngine = {
5 | init: (data: IXData) => void;
6 | };
7 | declare type InteractionProviderProps = {
8 | children: React.ReactNode;
9 | createEngine: () => IXEngine;
10 | };
11 | export declare const InteractionsProvider: React.FC;
12 | export declare const useInteractions: (
13 | data: IXData,
14 | styles?: CSSModules | undefined
15 | ) => void;
16 | export declare function triggerIXEvent(
17 | element: HTMLElement | null | undefined,
18 | active: boolean
19 | ): void;
20 | export declare function useIXEvent(
21 | element: HTMLElement | null | undefined,
22 | active: boolean
23 | ): void;
24 | export {};
25 |
--------------------------------------------------------------------------------
/devlink/interactions.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import {
3 | debounce,
4 | dispatchCustomEvent,
5 | replaceSelector,
6 | useLayoutEffect,
7 | } from "./utils";
8 | const enhanceIXData = (data, styles) => {
9 | const newIXData = structuredClone(data);
10 | for (const id in newIXData.events) {
11 | const { target, targets } = newIXData.events[id];
12 | for (const t of [target, ...targets]) {
13 | if (t.appliesTo !== "CLASS") continue;
14 | t.selector = replaceSelector(t.selector, styles);
15 | }
16 | }
17 | for (const id in newIXData.actionLists) {
18 | const { actionItemGroups, continuousParameterGroups } =
19 | newIXData.actionLists[id];
20 | if (actionItemGroups) {
21 | for (const { actionItems } of actionItemGroups) {
22 | for (const { config } of actionItems) {
23 | const { selector } = config.target;
24 | if (!selector) continue;
25 | config.target.selector = replaceSelector(selector, styles);
26 | }
27 | }
28 | }
29 | if (continuousParameterGroups) {
30 | for (const group of continuousParameterGroups) {
31 | for (const { actionItems } of group.continuousActionGroups) {
32 | for (const { config } of actionItems) {
33 | const { selector } = config.target;
34 | if (!selector) continue;
35 | config.target.selector = replaceSelector(selector, styles);
36 | }
37 | }
38 | }
39 | }
40 | }
41 | return newIXData;
42 | };
43 | const IXContext = React.createContext(null);
44 | export const InteractionsProvider = ({ children, createEngine }) => {
45 | const ixData = React.useRef({});
46 | const ixStyles = React.useRef();
47 | const ixEngine = React.useRef();
48 | const debouncedInit = React.useRef(
49 | debounce((data, styles) => {
50 | if (!ixEngine.current) ixEngine.current = createEngine();
51 | const newData = styles ? enhanceIXData(data, styles) : data;
52 | ixEngine.current.init(newData);
53 | dispatchCustomEvent(document, "IX2_PAGE_UPDATE");
54 | })
55 | );
56 | const initEngine = React.useCallback((data, styles) => {
57 | if (!ixData.current.site) {
58 | ixData.current.site = data.site;
59 | }
60 | ixData.current.events = {
61 | ...ixData.current.events,
62 | ...data.events,
63 | };
64 | ixData.current.actionLists = {
65 | ...ixData.current.actionLists,
66 | ...data.actionLists,
67 | };
68 | if (styles) {
69 | // Check if styles exist. If ixStyles.current is falsy, set it to an empty object
70 | ixStyles.current = ixStyles.current ?? {};
71 | // Loop through each property in the styles object
72 | for (const s in styles) {
73 | // Check if the current style is not already included in ixStyles.current
74 | if (!ixStyles.current[s]?.includes(styles[s])) {
75 | // Get the current style value from ixStyles.current
76 | const currentStyle = ixStyles.current[s];
77 | // Concatenate the new style with the current style (if it exists)
78 | ixStyles.current[s] =
79 | CSS.escape(styles[s]) + (currentStyle ? ` ${currentStyle}` : "");
80 | }
81 | }
82 | }
83 | debouncedInit.current(ixData.current, ixStyles.current);
84 | }, []);
85 | return {children};
86 | };
87 | export const useInteractions = (data, styles) => {
88 | const initEngine = React.useContext(IXContext);
89 | React.useEffect(() => {
90 | if (initEngine) initEngine(data, styles);
91 | }, [initEngine, data, styles]);
92 | React.useEffect(() => {
93 | if (document.querySelector("html")?.hasAttribute("data-wf-page")) return;
94 | const hasPageInteractions = Object.values(data.events).some(
95 | (event) => event.target.appliesTo === "PAGE"
96 | );
97 | if (hasPageInteractions) {
98 | document.documentElement.setAttribute("data-wf-page", "wf-page-id");
99 | dispatchCustomEvent(document, "IX2_PAGE_UPDATE");
100 | }
101 | }, [data.events]);
102 | };
103 | export function triggerIXEvent(element, active) {
104 | if (!element) return;
105 | dispatchCustomEvent(
106 | element,
107 | active ? "COMPONENT_ACTIVE" : "COMPONENT_INACTIVE"
108 | );
109 | }
110 | export function useIXEvent(element, active) {
111 | useLayoutEffect(() => {
112 | triggerIXEvent(element, active);
113 | }, [element, active]);
114 | }
115 |
--------------------------------------------------------------------------------
/devlink/types.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | export declare type CSSModules = {
3 | [sel: string]: string;
4 | };
5 | export declare namespace Basic {
6 | type PreloadedLink = {
7 | preload?: "prerender" | "prefetch" | "none";
8 | };
9 | type TargetedLink = {
10 | target?: "_self" | "_blank";
11 | };
12 | type PreloadedAndTargetedLink = PreloadedLink & TargetedLink;
13 | export type Link = {
14 | href: string;
15 | } & PreloadedAndTargetedLink;
16 | export type RichTextChildren = React.ReactNode;
17 | export {};
18 | }
19 | export declare namespace Asset {
20 | type Image = string;
21 | }
22 | export declare namespace Embed {
23 | type Video = {
24 | height: number;
25 | width: number;
26 | title: string;
27 | url: string;
28 | };
29 | }
30 | export declare namespace Visibility {
31 | type VisibilityConditions = boolean;
32 | }
33 |
--------------------------------------------------------------------------------
/devlink/types.js:
--------------------------------------------------------------------------------
1 | export {};
2 |
--------------------------------------------------------------------------------
/devlink/utils.d.ts:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { CSSModules } from "./types";
3 | /**
4 | * The cx function takes a css module object and the name of the element classes.
5 | * It will extract the final class names from the object if the class doesn't exist
6 | * on the object it will use the original name
7 | */
8 | export declare const cx: (style: CSSModules, ...classNames: string[]) => string;
9 | /**
10 | * The cj function takes care of eliminting classes that don't exist and joining
11 | * them with a white space.
12 | */
13 | export declare const cj: (
14 | ...classNames: (string | boolean | undefined)[]
15 | ) => string;
16 | export declare const removeUnescaped: (value: string) => string;
17 | /**
18 | * Function to replace the CSS classes in a given selector with the actual CSS values
19 | * using a given styles object (i.e. object with CSS class keys and CSS styles as values).
20 | */
21 | export declare const replaceSelector: (
22 | selector: string,
23 | styles: CSSModules
24 | ) => string;
25 | export declare function debounce void>(
26 | this: void,
27 | func: T,
28 | timeout?: number
29 | ): (...args: Parameters) => void;
30 | /**
31 | * cubic bezier functions have been extracted from this repository:
32 | * https://github.com/ai/easings.net
33 | */
34 | export declare const EASING_FUNCTIONS: {
35 | linear: string;
36 | ease: string;
37 | "ease-in": string;
38 | "ease-out": string;
39 | "ease-in-out": string;
40 | "ease-in-sine": string;
41 | "ease-out-sine": string;
42 | "ease-in-out-sine": string;
43 | "ease-in-quad": string;
44 | "ease-out-quad": string;
45 | "ease-in-out-quad": string;
46 | "ease-in-cubic": string;
47 | "ease-out-cubic": string;
48 | "ease-in-out-cubic": string;
49 | "ease-in-quart": string;
50 | "ease-out-quart": string;
51 | "ease-in-out-quart": string;
52 | "ease-in-quint": string;
53 | "ease-out-quint": string;
54 | "ease-in-out-quint": string;
55 | "ease-in-expo": string;
56 | "ease-out-expo": string;
57 | "ease-in-out-expo": string;
58 | "ease-in-circ": string;
59 | "ease-out-circ": string;
60 | "ease-in-out-circ": string;
61 | "ease-in-back": string;
62 | "ease-out-back": string;
63 | "ease-in-out-back": string;
64 | };
65 | export declare const isServer: boolean;
66 | export declare const useLayoutEffect: typeof React.useLayoutEffect;
67 | export declare function useResizeObserver(
68 | ref: React.RefObject,
69 | fn: (entry: ResizeObserverEntry) => void
70 | ): void;
71 | export declare function isUrl(str: string): boolean;
72 | export declare function loadScript(
73 | src: string,
74 | options?: {
75 | async?: boolean;
76 | type?: string;
77 | defer?: boolean;
78 | cacheRegex?: RegExp;
79 | }
80 | ): Promise;
81 | export declare const KEY_CODES: {
82 | ARROW_LEFT: string;
83 | ARROW_UP: string;
84 | ARROW_RIGHT: string;
85 | ARROW_DOWN: string;
86 | SPACE: string;
87 | ENTER: string;
88 | HOME: string;
89 | END: string;
90 | };
91 | export declare function dispatchCustomEvent(
92 | element: Document | Element,
93 | eventName: string
94 | ): void;
95 | export declare function useClickOut(
96 | ref: React.RefObject,
97 | action: () => void
98 | ): void;
99 | export declare function extractElement<
100 | T extends React.JSXElementConstructor
101 | >(
102 | elements: React.ReactNode[],
103 | type: T
104 | ): {
105 | extracted: React.ReactNode;
106 | tree: React.ReactNode[];
107 | };
108 |
--------------------------------------------------------------------------------
/devlink/utils.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | /**
3 | * The cx function takes a css module object and the name of the element classes.
4 | * It will extract the final class names from the object if the class doesn't exist
5 | * on the object it will use the original name
6 | */
7 | export const cx = (style, ...classNames) =>
8 | cj(...classNames.map((c) => style[c] ?? c));
9 | /**
10 | * The cj function takes care of eliminting classes that don't exist and joining
11 | * them with a white space.
12 | */
13 | export const cj = (...classNames) => classNames.filter(Boolean).join(" ");
14 | const UNESCAPED_CHARS = /(\\b|\\f|\\r\\n|\\n|\\r|\\t|\\v)/gm;
15 | export const removeUnescaped = (value) =>
16 | decodeURIComponent(value).replace(UNESCAPED_CHARS, "");
17 | /**
18 | * Regular expression to match CSS class selectors (i.e. starting with a '.').
19 | */
20 | const CSS_CLASS = /\.-?[_a-zA-Z]+[_a-zA-Z0-9-]*/g;
21 | /**
22 | * Regular expression to test for whitespace characters (i.e. spaces, tabs, line breaks) in a CSS style.
23 | */
24 | const WHITE_SPACE = /\s+/g;
25 | /**
26 | * Function to replace the CSS classes in a given selector with the actual CSS values
27 | * using a given styles object (i.e. object with CSS class keys and CSS styles as values).
28 | */
29 | export const replaceSelector = (selector, styles) => {
30 | return selector.replace(new RegExp(CSS_CLASS), (match) => {
31 | // Get the corresponding CSS style for the current class selector by removing the leading '.'
32 | const segment = styles[match.replace(".", "")];
33 | // If no corresponding CSS style was found for the current class selector, return the original selector.
34 | if (!segment) return match;
35 | // If the CSS style for the current class selector has whitespace characters, create a compound selector with
36 | // all the individual selectors wrapped in a ':is' pseudo-class.
37 | if (new RegExp(WHITE_SPACE).test(segment)) {
38 | const segmentSelector = segment
39 | .split(WHITE_SPACE)
40 | .map((w) => "." + w)
41 | .join(",");
42 | return `:is(${segmentSelector})`;
43 | }
44 | // If the CSS style for the current class selector does not have whitespace characters,
45 | // return a new selector with the class selector replaced by its corresponding CSS style.
46 | return `.${segment}`;
47 | });
48 | };
49 | export function debounce(func, timeout = 0) {
50 | let timer;
51 | return (...args) => {
52 | clearTimeout(timer);
53 | timer = setTimeout(() => {
54 | func.apply(this, args);
55 | }, timeout);
56 | };
57 | }
58 | /**
59 | * cubic bezier functions have been extracted from this repository:
60 | * https://github.com/ai/easings.net
61 | */
62 | export const EASING_FUNCTIONS = {
63 | linear: "linear",
64 | ease: "ease",
65 | "ease-in": "ease-in",
66 | "ease-out": "ease--out",
67 | "ease-in-out": "ease-in-out",
68 | "ease-in-sine": "cubic-bezier(0.12, 0, 0.39, 0)",
69 | "ease-out-sine": "cubic-bezier(0.61, 1, 0.88, 1)",
70 | "ease-in-out-sine": "cubic-bezier(0.37, 0, 0.63, 1)",
71 | "ease-in-quad": "cubic-bezier(0.11, 0, 0.5, 0)",
72 | "ease-out-quad": "cubic-bezier(0.5, 1, 0.89, 1)",
73 | "ease-in-out-quad": "cubic-bezier(0.45, 0, 0.55, 1)",
74 | "ease-in-cubic": "cubic-bezier(0.32, 0, 0.67, 0)",
75 | "ease-out-cubic": "cubic-bezier(0.33, 1, 0.68, 1)",
76 | "ease-in-out-cubic": "cubic-bezier(0.65, 0, 0.35, 1)",
77 | "ease-in-quart": "cubic-bezier(0.5, 0, 0.75, 0)",
78 | "ease-out-quart": "cubic-bezier(0.25, 1, 0.5, 1)",
79 | "ease-in-out-quart": "cubic-bezier(0.76, 0, 0.24, 1)",
80 | "ease-in-quint": "cubic-bezier(0.64, 0, 0.78, 0)",
81 | "ease-out-quint": "cubic-bezier(0.22, 1, 0.36, 1)",
82 | "ease-in-out-quint": "cubic-bezier(0.83, 0, 0.17, 1)",
83 | "ease-in-expo": "cubic-bezier(0.7, 0, 0.84, 0)",
84 | "ease-out-expo": "cubic-bezier(0.16, 1, 0.3, 1)",
85 | "ease-in-out-expo": "cubic-bezier(0.87, 0, 0.13, 1)",
86 | "ease-in-circ": "cubic-bezier(0.55, 0, 1, 0.45)",
87 | "ease-out-circ": "cubic-bezier(0, 0.55, 0.45, 1)",
88 | "ease-in-out-circ": "cubic-bezier(0.85, 0, 0.15, 1)",
89 | "ease-in-back": "cubic-bezier(0.36, 0, 0.66, -0.56)",
90 | "ease-out-back": "cubic-bezier(0.34, 1.56, 0.64, 1)",
91 | "ease-in-out-back": "cubic-bezier(0.68, -0.6, 0.32, 1.6)",
92 | };
93 | export const isServer = typeof window === "undefined";
94 | export const useLayoutEffect = isServer ? () => {} : React.useLayoutEffect;
95 | export function useResizeObserver(ref, fn) {
96 | const observer = React.useMemo(
97 | () => (isServer ? null : new ResizeObserver(([entry]) => fn(entry))),
98 | [fn]
99 | );
100 | React.useEffect(() => {
101 | const target = ref.current;
102 | if (!target) return;
103 | observer?.observe(ref.current);
104 | return () => observer?.unobserve(target);
105 | }, [ref, observer]);
106 | }
107 | export function isUrl(str) {
108 | if (!str) {
109 | return false;
110 | }
111 | if (str.indexOf('"') >= 0 || str.indexOf("'") >= 0) {
112 | return false;
113 | }
114 | return /((http|https):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?/i.test(
115 | str.trim()
116 | );
117 | }
118 | function isScriptPresent(regex) {
119 | return Array.from(document.scripts).reduce(
120 | (isPresent, script) => (isPresent ? isPresent : regex.test(script.src)),
121 | false
122 | );
123 | }
124 | export function loadScript(src, options) {
125 | return new Promise((resolve, reject) => {
126 | try {
127 | const {
128 | async = true,
129 | type = "text/javascript",
130 | defer = true,
131 | cacheRegex,
132 | } = options ?? {};
133 | if (!src)
134 | return reject({
135 | loaded: false,
136 | error: true,
137 | message: "No src provided",
138 | });
139 | if (cacheRegex && isScriptPresent(cacheRegex))
140 | return resolve({ loaded: true, error: false });
141 | const tag = document.createElement("script");
142 | const container = document.head || document.body;
143 | tag.type = type;
144 | tag.async = async;
145 | tag.src = src;
146 | tag.defer = defer;
147 | tag.addEventListener("load", () => {
148 | resolve({ loaded: true, error: false });
149 | });
150 | tag.addEventListener("error", () => {
151 | reject({
152 | loaded: false,
153 | error: true,
154 | message: `Failed to load script with src ${src}`,
155 | });
156 | });
157 | container.appendChild(tag);
158 | } catch (error) {
159 | reject(error);
160 | }
161 | });
162 | }
163 | export const KEY_CODES = {
164 | ARROW_LEFT: "ArrowLeft",
165 | ARROW_UP: "ArrowUp",
166 | ARROW_RIGHT: "ArrowRight",
167 | ARROW_DOWN: "ArrowDown",
168 | SPACE: " ",
169 | ENTER: "Enter",
170 | HOME: "Home",
171 | END: "End",
172 | };
173 | export function dispatchCustomEvent(element, eventName) {
174 | element.dispatchEvent(
175 | new CustomEvent(eventName, {
176 | bubbles: true,
177 | cancelable: true,
178 | })
179 | );
180 | }
181 | export function useClickOut(ref, action) {
182 | React.useEffect(() => {
183 | function handleClickOutside(event) {
184 | if (ref.current?.contains(event.target)) return;
185 | event.preventDefault();
186 | action();
187 | }
188 | document.addEventListener("mousedown", handleClickOutside);
189 | return () => {
190 | document.removeEventListener("mousedown", handleClickOutside);
191 | };
192 | }, [ref, action]);
193 | }
194 | export function extractElement(elements, type) {
195 | let extracted;
196 | // Use inner function to preserve scope so recursion is easier to read
197 | function removeElementByType(elements) {
198 | return elements.map((element) => {
199 | if (!React.isValidElement(element)) {
200 | return element;
201 | }
202 | if (element.type === type) {
203 | // We found the element we want to extract
204 | extracted = element;
205 | // We don't include it in the returned array, essentially removing it from the tree
206 | return null;
207 | }
208 | // Recursively process the children
209 | const children = removeElementByType(
210 | React.Children.toArray(element.props.children)
211 | );
212 | // Return a new element with the updated children
213 | return React.cloneElement(element, element.props, ...children);
214 | });
215 | }
216 | const tree = removeElementByType(elements);
217 | return { extracted, tree };
218 | }
219 |
--------------------------------------------------------------------------------
/fetchJob.js:
--------------------------------------------------------------------------------
1 | export async function fetchJob(id) {
2 | const options = {
3 | headers: {
4 | Authorization: `Bearer ${process.env.JOBS_KEY}`,
5 | },
6 | };
7 |
8 | try {
9 | const res = await fetch(
10 | `https://api.airtable.com/v0/${process.env.AIRTABLE_BASE_ID}/Jobs/${id}`,
11 | options
12 | );
13 | const data = await res.json();
14 | return data;
15 | } catch (e) {
16 | console.error(`An error occurred: ${e}`);
17 | throw e;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {
3 | reactStrictMode: true,
4 | }
5 |
6 | module.exports = nextConfig
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "job-board",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@types/node": "18.16.3",
13 | "@types/react": "18.2.0",
14 | "@types/react-dom": "18.2.1",
15 | "@webflow/webflow-cli": "^1.2.2",
16 | "chart.js": "^4.3.0",
17 | "dotenv": "^16.0.3",
18 | "eslint": "8.39.0",
19 | "eslint-config-next": "13.3.1",
20 | "next": "13.3.1",
21 | "react": "18.2.0",
22 | "react-chartjs-2": "^5.2.0",
23 | "react-dom": "18.2.0",
24 | "typescript": "5.0.4"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | import '@/styles/globals.css'
2 | import '@/devlink/global.css'
3 | import type { AppProps } from 'next/app'
4 | import { InteractionsProvider, createIX2Engine } from '@/devlink'
5 |
6 | export default function App({ Component, pageProps }: AppProps) {
7 | return (
8 |
9 |
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import { Html, Head, Main, NextScript } from 'next/document'
2 |
3 | export default function Document() {
4 | return (
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/pages/about.tsx:
--------------------------------------------------------------------------------
1 | import Head from 'next/head';
2 | import { Newnav, AboutHero, AboutInformation, Footer } from '@/devlink'
3 |
4 | export default function Jobs() {
5 | const copyYear = new Date().getFullYear();
6 | return (
7 | <>
8 |
9 | Visual Development Jobs
10 |
11 |
12 |
13 |
14 |
21 |
22 |
23 |
24 |
25 |
28 | >
29 | )
30 | }
--------------------------------------------------------------------------------
/pages/api/jobs.js:
--------------------------------------------------------------------------------
1 | export const config = {
2 | api: {
3 | externalResolver: true,
4 | },
5 | };
6 |
7 | export default function handler(req, res) {
8 | res.setHeader("Cache-Control", "s-maxage=300, stale-while-revalidate");
9 | const options = {
10 | headers: {
11 | Authorization: `Bearer ${process.env.JOBS_KEY}`,
12 | },
13 | };
14 | fetch(
15 | `https://api.airtable.com/v0/${process.env.AIRTABLE_BASE_ID}/Jobs?view=Grid%20view`,
16 | options
17 | )
18 | .then((response) => response.json())
19 | .then((usefulData) => {
20 | res.json(usefulData);
21 | })
22 | .catch((e) => {
23 | console.error(`An error occurred: ${e}`);
24 | });
25 | }
26 |
--------------------------------------------------------------------------------
/pages/api/jobs/[id].js:
--------------------------------------------------------------------------------
1 | export const config = {
2 | api: {
3 | externalResolver: true,
4 | },
5 | };
6 |
7 | export default function handler(req, res) {
8 | res.setHeader("Cache-Control", "s-maxage=300, stale-while-revalidate");
9 | const { id } = req.query;
10 | const options = {
11 | headers: {
12 | Authorization: `Bearer ${process.env.JOBS_KEY}`,
13 | },
14 | };
15 | fetch(
16 | `https://api.airtable.com/v0/${process.env.AIRTABLE_BASE_ID}/Jobs/${id}`,
17 | options
18 | )
19 | .then((response) => response.json())
20 | .then((usefulData) => {
21 | res.json(usefulData);
22 | })
23 | .catch((e) => {
24 | console.error(`An error occurred: ${e}`);
25 | });
26 | }
27 |
--------------------------------------------------------------------------------
/pages/api/jobs/featured.js:
--------------------------------------------------------------------------------
1 | export const config = {
2 | api: {
3 | externalResolver: true,
4 | },
5 | };
6 |
7 | export default function handler(req, res) {
8 | res.setHeader("Cache-Control", "s-maxage=300, stale-while-revalidate");
9 | const options = {
10 | headers: {
11 | Authorization: `Bearer ${process.env.JOBS_KEY}`,
12 | },
13 | };
14 | fetch(
15 | `https://api.airtable.com/v0/${process.env.AIRTABLE_BASE_ID}/Jobs?view=Featured`,
16 | options
17 | )
18 | .then((response) => response.json())
19 | .then((usefulData) => {
20 | res.json(usefulData);
21 | })
22 | .catch((e) => {
23 | console.error(`An error occurred: ${e}`);
24 | });
25 | }
26 |
--------------------------------------------------------------------------------
/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import Head from "next/head";
2 | import {
3 | JobListing,
4 | Newnav,
5 | Hero,
6 | Brands,
7 | Pricing,
8 | Features,
9 | Footer,
10 | Cta,
11 | Stats,
12 | PricingGrid
13 | } from "@/devlink";
14 | import { Chart } from "@/components";
15 | import { useState, useEffect } from "react";
16 |
17 | export default function Jobs() {
18 | const copyYear = new Date().getFullYear();
19 | const [jobsData, setJobsData] = useState([]);
20 | const [loading, setLoading] = useState(true);
21 | const [error, setError] = useState(null);
22 |
23 | useEffect(() => {
24 | fetch("api/jobs/featured")
25 | .then((response) => response.json())
26 | .then((data) => {
27 | setJobsData(data.records);
28 | })
29 | .catch((e) => {
30 | setError(e);
31 | console.error(`An error occurred: ${error}`);
32 | })
33 | .finally(() => {
34 | setLoading(false);
35 | });
36 | }, []);
37 | return (
38 | <>
39 |
40 | Visual Development Jobs
41 |
42 |
43 |
44 |
45 |
56 |
57 |
58 |
59 | }
63 | />
64 |
72 |
73 | }
75 | />
76 |
77 |
78 |
Featured Jobs
79 |
80 | {jobsData.map((job) => {
81 | return (
82 | -
83 |
95 |
96 | );
97 | })}
98 |
99 |
100 |
101 |
102 |
103 | >
104 | );
105 | }
106 |
--------------------------------------------------------------------------------
/pages/jobs.tsx:
--------------------------------------------------------------------------------
1 | import Head from 'next/head';
2 | import { JobListing, Newnav, Brands, PageHeading, Footer } from '@/devlink'
3 | import { useState, useEffect } from 'react';
4 |
5 | export default function Jobs() {
6 | const copyYear = new Date().getFullYear();
7 | const [jobsData, setJobsData] = useState([]);
8 | const [loading, setLoading] = useState(true);
9 | const [error, setError] = useState(null);
10 |
11 | useEffect(() => {
12 |
13 | fetch('api/jobs')
14 | .then((response) => response.json())
15 | .then((data) => {
16 | setJobsData(data.records);
17 | })
18 | .catch((e) => {
19 | setError(e);
20 | console.error(`An error occurred: ${error}`);
21 | }).finally(() => {
22 | setLoading(false);
23 | });
24 | }, []);
25 | return (
26 | <>
27 |
28 | Visual Development Jobs
29 |
30 |
31 |
32 |
33 |
40 |
41 |
42 |
43 |
44 |
45 | { jobsData.map((job) => {
46 | return -
56 |
57 | })}
58 |
59 |
60 |
61 |
62 |
65 | >
66 | )
67 | }
--------------------------------------------------------------------------------
/pages/jobs/[id].jsx:
--------------------------------------------------------------------------------
1 | import Head from "next/head";
2 | import { PageHeading, Newnav, Details, Footer } from "@/devlink";
3 | import { fetchJob } from "../../fetchJob";
4 |
5 | export const getServerSideProps = async (context) => {
6 | const id = context.params.id;
7 | const data = await fetchJob(id);
8 |
9 | return {
10 | props: { job: data },
11 | };
12 | };
13 |
14 | export default function Jobs({ job }) {
15 | const copyYear = new Date().getFullYear();
16 | return (
17 | <>
18 |
19 |
20 | {job.fields.Name} at {job.fields.Company}
21 |
22 |
23 |
24 |
25 |
26 |
37 |
38 |
39 |
48 |
49 |
50 | >
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Webflow-Examples/devlink-job-board/da2957754e038febcc0af2fb2a20cce9dc65eda2/public/favicon.ico
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/styles/Home.module.css:
--------------------------------------------------------------------------------
1 | .main {
2 | display: flex;
3 | flex-direction: column;
4 | justify-content: space-between;
5 | align-items: center;
6 | padding: 6rem;
7 | min-height: 100vh;
8 | }
9 |
10 | .description {
11 | display: inherit;
12 | justify-content: inherit;
13 | align-items: inherit;
14 | font-size: 0.85rem;
15 | max-width: var(--max-width);
16 | width: 100%;
17 | z-index: 2;
18 | font-family: var(--font-mono);
19 | }
20 |
21 | .description a {
22 | display: flex;
23 | justify-content: center;
24 | align-items: center;
25 | gap: 0.5rem;
26 | }
27 |
28 | .description p {
29 | position: relative;
30 | margin: 0;
31 | padding: 1rem;
32 | background-color: rgba(var(--callout-rgb), 0.5);
33 | border: 1px solid rgba(var(--callout-border-rgb), 0.3);
34 | border-radius: var(--border-radius);
35 | }
36 |
37 | .code {
38 | font-weight: 700;
39 | font-family: var(--font-mono);
40 | }
41 |
42 | .grid {
43 | display: grid;
44 | grid-template-columns: repeat(4, minmax(25%, auto));
45 | width: var(--max-width);
46 | max-width: 100%;
47 | }
48 |
49 | .card {
50 | padding: 1rem 1.2rem;
51 | border-radius: var(--border-radius);
52 | background: rgba(var(--card-rgb), 0);
53 | border: 1px solid rgba(var(--card-border-rgb), 0);
54 | transition: background 200ms, border 200ms;
55 | }
56 |
57 | .card span {
58 | display: inline-block;
59 | transition: transform 200ms;
60 | }
61 |
62 | .card h2 {
63 | font-weight: 600;
64 | margin-bottom: 0.7rem;
65 | }
66 |
67 | .card p {
68 | margin: 0;
69 | opacity: 0.6;
70 | font-size: 0.9rem;
71 | line-height: 1.5;
72 | max-width: 30ch;
73 | }
74 |
75 | .center {
76 | display: flex;
77 | justify-content: center;
78 | align-items: center;
79 | position: relative;
80 | padding: 4rem 0;
81 | }
82 |
83 | .center::before {
84 | background: var(--secondary-glow);
85 | border-radius: 50%;
86 | width: 480px;
87 | height: 360px;
88 | margin-left: -400px;
89 | }
90 |
91 | .center::after {
92 | background: var(--primary-glow);
93 | width: 240px;
94 | height: 180px;
95 | z-index: -1;
96 | }
97 |
98 | .center::before,
99 | .center::after {
100 | content: '';
101 | left: 50%;
102 | position: absolute;
103 | filter: blur(45px);
104 | transform: translateZ(0);
105 | }
106 |
107 | .logo {
108 | position: relative;
109 | }
110 | /* Enable hover only on non-touch devices */
111 | @media (hover: hover) and (pointer: fine) {
112 | .card:hover {
113 | background: rgba(var(--card-rgb), 0.1);
114 | border: 1px solid rgba(var(--card-border-rgb), 0.15);
115 | }
116 |
117 | .card:hover span {
118 | transform: translateX(4px);
119 | }
120 | }
121 |
122 | @media (prefers-reduced-motion) {
123 | .card:hover span {
124 | transform: none;
125 | }
126 | }
127 |
128 | /* Mobile */
129 | @media (max-width: 700px) {
130 | .content {
131 | padding: 4rem;
132 | }
133 |
134 | .grid {
135 | grid-template-columns: 1fr;
136 | margin-bottom: 120px;
137 | max-width: 320px;
138 | text-align: center;
139 | }
140 |
141 | .card {
142 | padding: 1rem 2.5rem;
143 | }
144 |
145 | .card h2 {
146 | margin-bottom: 0.5rem;
147 | }
148 |
149 | .center {
150 | padding: 8rem 0 6rem;
151 | }
152 |
153 | .center::before {
154 | transform: none;
155 | height: 300px;
156 | }
157 |
158 | .description {
159 | font-size: 0.8rem;
160 | }
161 |
162 | .description a {
163 | padding: 1rem;
164 | }
165 |
166 | .description p,
167 | .description div {
168 | display: flex;
169 | justify-content: center;
170 | position: fixed;
171 | width: 100%;
172 | }
173 |
174 | .description p {
175 | align-items: center;
176 | inset: 0 0 auto;
177 | padding: 2rem 1rem 1.4rem;
178 | border-radius: 0;
179 | border: none;
180 | border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25);
181 | background: linear-gradient(
182 | to bottom,
183 | rgba(var(--background-start-rgb), 1),
184 | rgba(var(--callout-rgb), 0.5)
185 | );
186 | background-clip: padding-box;
187 | backdrop-filter: blur(24px);
188 | }
189 |
190 | .description div {
191 | align-items: flex-end;
192 | pointer-events: none;
193 | inset: auto 0 0;
194 | padding: 2rem;
195 | height: 200px;
196 | background: linear-gradient(
197 | to bottom,
198 | transparent 0%,
199 | rgb(var(--background-end-rgb)) 40%
200 | );
201 | z-index: 1;
202 | }
203 | }
204 |
205 | /* Tablet and Smaller Desktop */
206 | @media (min-width: 701px) and (max-width: 1120px) {
207 | .grid {
208 | grid-template-columns: repeat(2, 50%);
209 | }
210 | }
211 |
212 | @media (prefers-color-scheme: dark) {
213 | .vercelLogo {
214 | filter: invert(1);
215 | }
216 |
217 | .logo {
218 | filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70);
219 | }
220 | }
221 |
222 | @keyframes rotate {
223 | from {
224 | transform: rotate(360deg);
225 | }
226 | to {
227 | transform: rotate(0deg);
228 | }
229 | }
230 |
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | * {
2 | -webkit-font-smoothing: antialiased;
3 | -moz-osx-font-smoothing: grayscale;
4 | }
5 |
6 | .section {
7 | padding-top: 5em;
8 | padding-bottom: 5em;
9 | }
10 |
11 | .container {
12 | width: 100%;
13 | max-width: 1200px;
14 | margin-right: auto;
15 | margin-left: auto;
16 | padding-right: 1em;
17 | padding-left: 1em;
18 | }
19 |
20 | .container.narrow {
21 | width: 100%;
22 | max-width: 720px;
23 | margin-right: auto;
24 | margin-left: auto;
25 | padding-right: 1em;
26 | padding-left: 1em;
27 | }
28 |
29 | .container:small {
30 | padding-right: 1.5em;
31 | padding-left: 1.5em;
32 | }
33 |
34 | .section-header {
35 | text-align: center;
36 | margin-bottom: 10px;
37 | }
38 |
39 | .container > ul {
40 | list-style: none;
41 | padding: 0;
42 | margin: 0;
43 | }
44 |
45 | @media (max-width: 768px) {
46 | .hero-heading {
47 | font-size: clamp(3rem, 14vw, 14vw);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "forceConsistentCasingInFileNames": true,
9 | "noEmit": true,
10 | "esModuleInterop": true,
11 | "module": "esnext",
12 | "moduleResolution": "node",
13 | "resolveJsonModule": true,
14 | "isolatedModules": true,
15 | "jsx": "preserve",
16 | "incremental": true,
17 | "paths": {
18 | "@/*": ["./*"]
19 | }
20 | },
21 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
22 | "exclude": ["node_modules"]
23 | }
24 |
--------------------------------------------------------------------------------