@codecavepro/brand 2.2.0 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -36
- package/dist/src/{components/common → common}/ArticlePreview.vue +3 -3
- package/dist/src/{components/common → common}/BrandNav.vue +1 -1
- package/dist/src/{components/common → common}/Button.vue +1 -1
- package/dist/src/{components/common → common}/Checkbox.vue +2 -2
- package/dist/src/common/HeaderBrows.vue +12 -0
- package/dist/src/{components/common → common}/InputText.vue +1 -1
- package/dist/src/{components/common → common}/Radio.vue +14 -10
- package/dist/src/{components/common → common}/Review.vue +2 -2
- package/dist/src/{components/common → common}/forms/ContactUsForm.vue +4 -4
- package/dist/src/{components/common → common}/forms/ResearchForm.vue +1 -1
- package/dist/src/footer/FooterCta.vue +26 -0
- package/dist/src/{components/header → header}/mobile-menu.vue +2 -2
- package/dist/src/{components/homepage → homepage}/technologies.vue +2 -2
- package/dist/src/{components/homepage → homepage}/technology-card.vue +17 -7
- package/dist/theme.css +1 -1
- package/dist/tokens.css +1 -1
- package/package.json +2 -2
- /package/dist/src/{components/common → common}/GlowButton.vue +0 -0
- /package/dist/src/{components/common → common}/TextField.vue +0 -0
- /package/dist/src/{components/common → common}/effects/TypingEffect.vue +0 -0
- /package/dist/src/{components/common → common}/images/LazyImage.vue +0 -0
- /package/dist/src/{components/footer → footer}/link-group.vue +0 -0
- /package/dist/src/{components/header → header}/services-list.vue +0 -0
- /package/dist/src/{components/project → project}/pain-points-item.vue +0 -0
package/README.md
CHANGED
|
@@ -6,10 +6,10 @@ wrapper layer.
|
|
|
6
6
|
|
|
7
7
|
Link one stylesheet and the whole system is live on `:root`.
|
|
8
8
|
|
|
9
|
-
> **Status: stable.** The token
|
|
10
|
-
> [codecave.pro](https://codecave.pro) ships, and the package
|
|
9
|
+
> **Status: stable.** The token _values_ mirror what
|
|
10
|
+
> [codecave.pro](https://codecave.pro) ships, and the package _layout_ — export paths,
|
|
11
11
|
> module shape — is settled under semver: an export will not move or disappear outside a
|
|
12
|
-
> major bump. Token
|
|
12
|
+
> major bump. Token _values_ can change in a minor or patch release, because they track a
|
|
13
13
|
> living design system; pin an exact version if you need them frozen.
|
|
14
14
|
|
|
15
15
|
## Install
|
|
@@ -48,7 +48,7 @@ redesign turns into a find-and-replace across your codebase.
|
|
|
48
48
|
`@codecavepro/brand/css` is the design system **whole**: the tokens, ten `@font-face`
|
|
49
49
|
rules, base rules for `html`, `body`, `h1`–`h6` and `a`, two layout primitives and
|
|
50
50
|
around sixty component classes. That is what you want for a page that should look like
|
|
51
|
-
CODECAVE. It is
|
|
51
|
+
CODECAVE. It is _not_ what you want in an app that already has a base layer — dropping
|
|
52
52
|
it in restyles every heading and link you have.
|
|
53
53
|
|
|
54
54
|
For that case there is a second stylesheet holding the custom properties and nothing
|
|
@@ -60,7 +60,7 @@ else:
|
|
|
60
60
|
|
|
61
61
|
Same 103 properties on `:root`, same values, zero rules. Nothing it declares can change
|
|
62
62
|
how a single existing element renders — a `var()` only takes effect where you write
|
|
63
|
-
one. Fonts come with it only as a
|
|
63
|
+
one. Fonts come with it only as a _name_: `--font-sans` says Satoshi, and declaring the
|
|
64
64
|
faces stays your call (see below).
|
|
65
65
|
|
|
66
66
|
Use it to end a duplicated palette without signing up for a redesign in the same commit.
|
|
@@ -70,7 +70,7 @@ Use it to end a duplicated palette without signing up for a redesign in the same
|
|
|
70
70
|
The same file is served, byte for byte, from the published design system:
|
|
71
71
|
|
|
72
72
|
```html
|
|
73
|
-
<link rel="stylesheet" href="https://brand.codecave.pro/colors_and_type.css"
|
|
73
|
+
<link rel="stylesheet" href="https://brand.codecave.pro/colors_and_type.css" />
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
The package build asserts that identity, so the file in your `node_modules` and the
|
|
@@ -81,26 +81,26 @@ file at that URL are provably the same bytes.
|
|
|
81
81
|
`@codecavepro/brand/css` declares ten `@font-face` rules for **Satoshi**, but **no font
|
|
82
82
|
binaries are included** — that is a licensing question, not an oversight. Until you
|
|
83
83
|
supply the files, the faces 404 and the browser falls back down the stack
|
|
84
|
-
(`-apple-system`, `Segoe UI`, Roboto, …). Tokens, colours and the type
|
|
84
|
+
(`-apple-system`, `Segoe UI`, Roboto, …). Tokens, colours and the type _scale_ are all
|
|
85
85
|
correct regardless; only the typeface is missing.
|
|
86
86
|
|
|
87
87
|
The two stylesheets expect the files in **different places**, because one is meant to be
|
|
88
88
|
dropped into a project on its own:
|
|
89
89
|
|
|
90
|
-
| Import
|
|
91
|
-
|
|
92
|
-
| `@codecavepro/brand/css`
|
|
93
|
-
| `@codecavepro/brand/fonts.css`
|
|
94
|
-
| `@codecavepro/brand/tokens.css` |
|
|
90
|
+
| Import | `@font-face` URLs | Put the `.woff2`/`.woff` files at |
|
|
91
|
+
| ------------------------------- | ----------------------------- | ---------------------------------------------- |
|
|
92
|
+
| `@codecavepro/brand/css` | `./fonts/Satoshi-*.woff2` | a `fonts/` directory **beside** the stylesheet |
|
|
93
|
+
| `@codecavepro/brand/fonts.css` | `./Satoshi-*.woff2` | **beside** the stylesheet itself |
|
|
94
|
+
| `@codecavepro/brand/tokens.css` | _none — it declares no faces_ | wherever your own `@font-face` rules point |
|
|
95
95
|
|
|
96
96
|
Get the cuts from [Fontshare](https://www.fontshare.com/fonts/satoshi) — 300, 400, 500,
|
|
97
97
|
700 and 900, each upright and italic, which is the ten faces these stylesheets declare.
|
|
98
98
|
Bind each with a real `font-weight` descriptor rather than letting the browser
|
|
99
99
|
synthesize; the design system documents why in
|
|
100
|
-
[DESIGN.md §10.3](https://github.com/CodeCavePro/brand/blob/development/
|
|
100
|
+
[DESIGN.md §10.3](https://github.com/CodeCavePro/brand/blob/development/DESIGN.md#103-synthesized-vs-real-font-weights).
|
|
101
101
|
|
|
102
102
|
**Do not use the `stylesheet.css` that comes in the Fontshare download.** It declares
|
|
103
|
-
Bold and Black
|
|
103
|
+
Bold and Black _both_ as `font-weight: bold`, and their italics likewise, so four cuts
|
|
104
104
|
collide into two slots and whichever is declared last silently wins — your `900` text
|
|
105
105
|
renders Bold, or your `700` renders Black, with nothing to indicate which. Take the
|
|
106
106
|
binaries from that download and the declarations from here.
|
|
@@ -111,13 +111,19 @@ For consumers that cannot read a stylesheet — design tooling, canvas/WebGL ren
|
|
|
111
111
|
PDF and email builders, native apps:
|
|
112
112
|
|
|
113
113
|
```ts
|
|
114
|
-
import {
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
import {
|
|
115
|
+
color,
|
|
116
|
+
radius,
|
|
117
|
+
fontSize,
|
|
118
|
+
spacing,
|
|
119
|
+
gradientBrand,
|
|
120
|
+
} from "@codecavepro/brand";
|
|
121
|
+
|
|
122
|
+
color.action; // '#5F20FE'
|
|
117
123
|
color.surfaceSecondary; // '#0F0F15'
|
|
118
|
-
radius.card;
|
|
119
|
-
fontSize.headingLg;
|
|
120
|
-
fontSize.body;
|
|
124
|
+
radius.card; // '1.5rem'
|
|
125
|
+
fontSize.headingLg; // { size: '3.5rem', lineHeight: '130%' }
|
|
126
|
+
fontSize.body; // { size: '1rem', lineHeight: '1.5rem' }
|
|
121
127
|
spacing.sectionPaddingTop; // '12.5rem'
|
|
122
128
|
```
|
|
123
129
|
|
|
@@ -131,14 +137,14 @@ Everything is `as const`, so the values are literal types, and the key unions ar
|
|
|
131
137
|
exported for building your own maps:
|
|
132
138
|
|
|
133
139
|
```ts
|
|
134
|
-
import type { SemanticColor, Radius, FontSizeStep } from
|
|
140
|
+
import type { SemanticColor, Radius, FontSizeStep } from "@codecavepro/brand";
|
|
135
141
|
```
|
|
136
142
|
|
|
137
|
-
| Module
|
|
138
|
-
|
|
139
|
-
| colour
|
|
140
|
-
| layout
|
|
141
|
-
| typography | `fontFamily`, `fontSize`, `fontWeight`, `eyebrow`
|
|
143
|
+
| Module | Exports |
|
|
144
|
+
| ---------- | ---------------------------------------------------------------------------------- |
|
|
145
|
+
| colour | `brand`, `gray`, `accent`, `technologyGradient`, `error`, `color`, `gradientBrand` |
|
|
146
|
+
| layout | `radius`, `gutter`, `spacing`, `shadow`, `maxWidthDesktop`, `breakpointSm` |
|
|
147
|
+
| typography | `fontFamily`, `fontSize`, `fontWeight`, `eyebrow` |
|
|
142
148
|
|
|
143
149
|
Two things to know about it:
|
|
144
150
|
|
|
@@ -149,7 +155,7 @@ Two things to know about it:
|
|
|
149
155
|
|
|
150
156
|
## The components
|
|
151
157
|
|
|
152
|
-
|
|
158
|
+
21 components and 13 icons — the buttons, the form controls and the forms
|
|
153
159
|
themselves, the nav bar, the footer link group, the article and technology
|
|
154
160
|
cards, the review card and the typing effect codecave.pro renders — ship as
|
|
155
161
|
**source**, byte-for-byte the files the site builds from. Not a reimplementation
|
|
@@ -168,8 +174,11 @@ the rest could follow.
|
|
|
168
174
|
|
|
169
175
|
```vue
|
|
170
176
|
<BrandNav
|
|
171
|
-
:left="[
|
|
172
|
-
|
|
177
|
+
:left="[
|
|
178
|
+
{ name: 'Workflow', href: '/workflow' },
|
|
179
|
+
{ name: 'Services', slot: 'services' },
|
|
180
|
+
]"
|
|
181
|
+
:right="[{ name: 'Insights', href: '/insights' }]"
|
|
173
182
|
:logo="wordmarkUrl"
|
|
174
183
|
current="Workflow"
|
|
175
184
|
>
|
|
@@ -184,9 +193,9 @@ resolve through your own asset pipeline.
|
|
|
184
193
|
|
|
185
194
|
```vue
|
|
186
195
|
<script setup lang="ts">
|
|
187
|
-
import Button from
|
|
188
|
-
import TextField from
|
|
189
|
-
import CloudIcon from
|
|
196
|
+
import Button from "@codecavepro/brand/components/common/Button.vue";
|
|
197
|
+
import TextField from "@codecavepro/brand/components/common/TextField.vue";
|
|
198
|
+
import CloudIcon from "@codecavepro/brand/assets/icons/cloud-icon.vue";
|
|
190
199
|
</script>
|
|
191
200
|
|
|
192
201
|
<template>
|
|
@@ -212,7 +221,7 @@ components; leave one out and only those are affected — `gsap` by `GlowButton.
|
|
|
212
221
|
Every colour, radius and control height in these components is a token — but they reach
|
|
213
222
|
most of them through **Tailwind utility classes** (`bg-surface-primary`,
|
|
214
223
|
`text-heading-md`, `rounded-custom`), and a utility class exists only if Tailwind knows
|
|
215
|
-
the name. `tokens.css` gives you the
|
|
224
|
+
the name. `tokens.css` gives you the _values_; `theme.css` gives Tailwind the names, so
|
|
216
225
|
you need both:
|
|
217
226
|
|
|
218
227
|
```css
|
|
@@ -271,7 +280,10 @@ three that show images take an optional resolver and default to leaving the URL
|
|
|
271
280
|
<Review :item="testimonial" />
|
|
272
281
|
|
|
273
282
|
<!-- URLs relative to your own media host -->
|
|
274
|
-
<Review
|
|
283
|
+
<Review
|
|
284
|
+
:item="testimonial"
|
|
285
|
+
:resolve-image="(u) => `https://cdn.example.com/${u}`"
|
|
286
|
+
/>
|
|
275
287
|
```
|
|
276
288
|
|
|
277
289
|
Their props are declared as the fields each one reads — `item.photo.url`,
|
|
@@ -296,7 +308,7 @@ the rules, the reasoning, the anti-patterns — is documented separately:
|
|
|
296
308
|
|
|
297
309
|
- **[brand.codecave.pro](https://brand.codecave.pro/)** — the published design system:
|
|
298
310
|
brand kit, foundation specimens, and a storybook of real components.
|
|
299
|
-
- **[DESIGN.md](https://github.com/CodeCavePro/brand/blob/development/
|
|
311
|
+
- **[DESIGN.md](https://github.com/CodeCavePro/brand/blob/development/DESIGN.md)** —
|
|
300
312
|
the rules in full: the 26-step ramp, components, motion, anti-patterns, known
|
|
301
313
|
divergences from what the site ships today.
|
|
302
314
|
- **[CodeCavePro/brand](https://github.com/CodeCavePro/brand)** — the repository.
|
|
@@ -306,7 +318,7 @@ the rules, the reasoning, the anti-patterns — is documented separately:
|
|
|
306
318
|
It authors nothing. Every byte is copied, extracted or compiled out of `docs/` in the
|
|
307
319
|
repository above: `css` and `fonts.css` are copied verbatim, `tokens.css` is extracted
|
|
308
320
|
from the same file `css` is copied from, `theme.css` is extracted from the capture of
|
|
309
|
-
codecave.pro's `global.css`, and the typed module is compiled from `
|
|
321
|
+
codecave.pro's `global.css`, and the typed module is compiled from `src/tokens/*.ts`.
|
|
310
322
|
A name that `theme.css` and `tokens.css` both give a literal value must agree, and the
|
|
311
323
|
build fails if it does not — otherwise one of the two would be dead and nobody would
|
|
312
324
|
see it. `docs/` stays the single origin, and CI asserts on every push that
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { formattedDate } from "
|
|
2
|
+
import { formattedDate } from "../helpers/date-formatter.ts";
|
|
3
3
|
|
|
4
4
|
/* The fields this component actually reads, instead of the generated Strapi
|
|
5
5
|
* `Article`. TypeScript is structural, so a real Article still satisfies this
|
|
@@ -32,12 +32,12 @@ const imageUrl = (url: string) => props.resolveImage?.(url) ?? url
|
|
|
32
32
|
|
|
33
33
|
<template>
|
|
34
34
|
<a :href="`${basePath ?? ''}${article.slug}/`" :class="`mx-1 lg:mx-2 w-full h-full self-start sm:self-auto p-6 flex flex-col gap-5 sm:gap-8
|
|
35
|
-
rounded-[2.25rem] bg-surface-secondary hover:bg-surface-
|
|
35
|
+
rounded-[2.25rem] bg-surface-secondary hover:bg-surface-tertiary transition-colors cursor-pointer border-surface-tertiary border
|
|
36
36
|
${className ?? ''}`">
|
|
37
37
|
<div class="flex flex-col sm:flex-row gap-5 sm:gap-8 h-fit">
|
|
38
38
|
<img loading="lazy" class="sm:w-[132px] sm:h-[132px] rounded-xl object-cover"
|
|
39
39
|
:src="imageUrl(article.cover.url)"
|
|
40
|
-
:alt="article.
|
|
40
|
+
:alt="article.title"
|
|
41
41
|
:width="100"
|
|
42
42
|
:height="100"/>
|
|
43
43
|
<div class="space-y-2 sm:space-y-3">
|
|
@@ -229,7 +229,7 @@ withDefaults(defineProps<{
|
|
|
229
229
|
visibility: hidden;
|
|
230
230
|
border-radius: 1rem;
|
|
231
231
|
padding: 1.25rem;
|
|
232
|
-
background: var(--color-surface-primary
|
|
232
|
+
background: var(--color-surface-primary);
|
|
233
233
|
-webkit-backdrop-filter: blur(24px);
|
|
234
234
|
backdrop-filter: blur(24px);
|
|
235
235
|
transition: opacity 0.2s ease, visibility 0.2s;
|
|
@@ -29,7 +29,7 @@ const variantClass = computed(() => {
|
|
|
29
29
|
case 'link':
|
|
30
30
|
return `${linkBaseClass} text-hovered underline`
|
|
31
31
|
default:
|
|
32
|
-
return `${buttonBaseClass} bg-primary-500 hover:bg-primary-700 active:bg-primary-900`
|
|
32
|
+
return `${buttonBaseClass} px-6 py-1 bg-primary-500 hover:bg-primary-700 active:bg-primary-900`
|
|
33
33
|
}
|
|
34
34
|
})
|
|
35
35
|
</script>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from "vue";
|
|
3
|
-
import AsteriskIcon from "
|
|
3
|
+
import AsteriskIcon from "../assets/icons/asterisk-icon.vue";
|
|
4
4
|
|
|
5
5
|
const props = withDefaults(
|
|
6
6
|
defineProps<{
|
|
@@ -101,7 +101,7 @@ input::before {
|
|
|
101
101
|
content: '';
|
|
102
102
|
transform: scale(0);
|
|
103
103
|
transition: var(--duration-control) transform ease-in-out;
|
|
104
|
-
background-image: url("
|
|
104
|
+
background-image: url("../assets/images/checked-icon.svg");
|
|
105
105
|
background-repeat: no-repeat;
|
|
106
106
|
background-position: center center;
|
|
107
107
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="flex flex-col md:flex-row gap-4 md:gap-6 text-lg md:text-xl">
|
|
3
|
+
<div class="space-x-1">
|
|
4
|
+
<span class="font-bold text-body-primary">15+</span>
|
|
5
|
+
<span class="text-body-secondary">Years of experience</span>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="space-x-1">
|
|
8
|
+
<span class="font-bold text-body-primary">4.7</span>
|
|
9
|
+
<span class="text-body-secondary">Average rating</span>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
@@ -5,21 +5,25 @@ const props = defineProps<{
|
|
|
5
5
|
id: string
|
|
6
6
|
label: string
|
|
7
7
|
name: string
|
|
8
|
-
isChecked?: boolean
|
|
9
8
|
modelValue?: string
|
|
10
9
|
variant?: 'primary' | 'secondary'
|
|
11
10
|
}>()
|
|
11
|
+
|
|
12
|
+
const emit = defineEmits<{'update:modelValue': [value: string]}>()
|
|
13
|
+
|
|
12
14
|
const baseLabelClass = 'w-fit flex items-center cursor-pointer text-body-primary transition-colors'
|
|
15
|
+
|
|
13
16
|
const labelClass = computed(() => {
|
|
14
17
|
switch (props.variant) {
|
|
15
18
|
case 'secondary':
|
|
16
|
-
return `${baseLabelClass} gap-3 p-3 pr-4 bg-surface-primary-transparent rounded-custom border-2 border-surface-quaternary
|
|
19
|
+
return `${baseLabelClass} gap-3 p-3 pr-4 bg-surface-primary-transparent rounded-custom border-2 border-surface-quaternary hover:bg-surface-tertiary`
|
|
17
20
|
default:
|
|
18
21
|
return `${baseLabelClass} gap-2 py-2 px-3 bg-surface-secondary rounded-lg`
|
|
19
22
|
}
|
|
20
23
|
})
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
const value = computed(() => props.id)
|
|
26
|
+
const isChecked = computed(() => props.modelValue === value.value)
|
|
23
27
|
</script>
|
|
24
28
|
|
|
25
29
|
<template>
|
|
@@ -28,13 +32,13 @@ defineEmits(['update:modelValue'])
|
|
|
28
32
|
:class="[labelClass]"
|
|
29
33
|
>
|
|
30
34
|
<input
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
:id="id"
|
|
36
|
+
type="radio"
|
|
37
|
+
:name="name"
|
|
38
|
+
:value="value"
|
|
39
|
+
class="bg-transparent border-[2px] border-surface-quaternary checked:border-action rounded-full hover:border-action"
|
|
40
|
+
:checked="isChecked"
|
|
41
|
+
@change="emit('update:modelValue', value)"
|
|
38
42
|
/>
|
|
39
43
|
<span class="text-xs whitespace-nowrap">
|
|
40
44
|
{{ label }}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import LinkedinIcon from "
|
|
3
|
-
import VerifiedIcon from "
|
|
2
|
+
import LinkedinIcon from "../assets/icons/linkedin-icon.vue";
|
|
3
|
+
import VerifiedIcon from "../assets/icons/verified-icon.vue";
|
|
4
4
|
import LazyImage from "./images/LazyImage.vue"
|
|
5
5
|
|
|
6
6
|
/* See ArticlePreview.vue: the fields read here, not the generated Strapi
|
|
@@ -5,16 +5,16 @@ import Checkbox from "../Checkbox.vue";
|
|
|
5
5
|
import Radio from "../Radio.vue";
|
|
6
6
|
import TextField from "../TextField.vue";
|
|
7
7
|
import { computed, ref, watch } from "vue";
|
|
8
|
-
import SuccessIcon from "
|
|
9
|
-
import CloseIcon from "
|
|
8
|
+
import SuccessIcon from "../../assets/icons/success-icon.vue";
|
|
9
|
+
import CloseIcon from "../../assets/icons/close-icon.vue";
|
|
10
10
|
import gsap from "gsap";
|
|
11
11
|
import type {
|
|
12
12
|
ContactFormDefinition,
|
|
13
13
|
ContactFormValues,
|
|
14
14
|
CrmSubmitResult,
|
|
15
15
|
ICrmFormClient,
|
|
16
|
-
} from "
|
|
17
|
-
import { isCorrectEmailFormat, isCorrectLinkedInFormat } from "
|
|
16
|
+
} from "../../lib/crm/types";
|
|
17
|
+
import { isCorrectEmailFormat, isCorrectLinkedInFormat } from "../../helpers/form-validator.ts";
|
|
18
18
|
|
|
19
19
|
/* This component knows nothing about HubSpot, or about any other CRM. It is
|
|
20
20
|
* handed the labels to render and something that can accept a submission; who
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { ref } from 'vue'
|
|
3
3
|
import InputText from '../InputText.vue'
|
|
4
4
|
import Button from '../Button.vue'
|
|
5
|
-
import { isCorrectEmailFormat } from "
|
|
5
|
+
import { isCorrectEmailFormat } from "../../helpers/form-validator.ts";
|
|
6
6
|
|
|
7
7
|
const email = ref('')
|
|
8
8
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import Button from "../common/Button.vue";
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<section
|
|
7
|
+
class="p-8 md:p-10 flex flex-col md:flex-row items-center justify-between gap-12 md:gap-7 rounded-3xl bg-primary-800"
|
|
8
|
+
>
|
|
9
|
+
<div class="flex-1">
|
|
10
|
+
<h3 class="text-heading font-bold text-[32px]">
|
|
11
|
+
Grab our AI trends research!
|
|
12
|
+
</h3>
|
|
13
|
+
|
|
14
|
+
<p class="md:mt-1 mt-4 text-body-secondary-lighter text-pretty">
|
|
15
|
+
We’ve researched current state of the AI in 3 industries so you don’t have to.
|
|
16
|
+
<br>
|
|
17
|
+
Check what we’ve found about yours.
|
|
18
|
+
</p>
|
|
19
|
+
</div>
|
|
20
|
+
|
|
21
|
+
<Button
|
|
22
|
+
title="Grab Research"
|
|
23
|
+
class="px-6"
|
|
24
|
+
/>
|
|
25
|
+
</section>
|
|
26
|
+
</template>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import Button from "../common/Button.vue";
|
|
3
|
-
import Shevron from "
|
|
3
|
+
import Shevron from "../assets/icons/shevron.vue";
|
|
4
4
|
import { computed, onUnmounted, ref, watch } from "vue";
|
|
5
|
-
import BackIcon from "
|
|
5
|
+
import BackIcon from "../assets/icons/back-icon.vue";
|
|
6
6
|
import ServicesList from "./services-list.vue";
|
|
7
7
|
|
|
8
8
|
/* Everything this drawer used to read out of menu.ts, paths.ts and the site's
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import { computed, ref } from "vue";
|
|
3
3
|
import { Carousel, type CarouselConfig, Slide } from "vue3-carousel";
|
|
4
4
|
import "vue3-carousel/carousel.css";
|
|
5
|
-
import { BREAKPOINTS } from "
|
|
5
|
+
import { BREAKPOINTS } from "../helpers/breakpoints.ts";
|
|
6
6
|
import GlowButton from "../common/GlowButton.vue";
|
|
7
7
|
import TypingEffect from "../common/effects/TypingEffect.vue";
|
|
8
8
|
import TechnologyCard from "./technology-card.vue";
|
|
9
|
-
import { carouselGestureLocker } from "
|
|
9
|
+
import { carouselGestureLocker } from "../helpers/carousel-gesture-locker.ts";
|
|
10
10
|
|
|
11
11
|
/* See ArticlePreview.vue: the fields read here, not the generated Strapi
|
|
12
12
|
* `Technology`, which a caller may still pass unchanged. This component
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import Button from "../common/Button.vue";
|
|
3
|
+
import { computed } from 'vue'
|
|
3
4
|
|
|
4
|
-
defineProps<{
|
|
5
|
+
const props = defineProps<{
|
|
5
6
|
active: boolean
|
|
6
7
|
name: string
|
|
7
|
-
/* Where "Explore service" goes. The card used to switch on `name` over this
|
|
8
|
-
* site's six service routes; that map is now @helpers/service-links.ts and
|
|
9
|
-
* the caller passes the result, so the card reaches no route table. An empty
|
|
10
|
-
* string renders the button inert, exactly as the switch's default did. */
|
|
11
8
|
href?: string
|
|
12
9
|
className?: string
|
|
13
10
|
index?: number
|
|
@@ -30,11 +27,24 @@ const translate = [
|
|
|
30
27
|
'xl:-translate-y-1/2',
|
|
31
28
|
'xl:-translate-y-1/2',
|
|
32
29
|
]
|
|
30
|
+
|
|
31
|
+
const cardClass = computed(() => [
|
|
32
|
+
'rounded-3xl',
|
|
33
|
+
'card-wrapper',
|
|
34
|
+
'cursor-pointer',
|
|
35
|
+
'select-none',
|
|
36
|
+
'absolute',
|
|
37
|
+
'transform',
|
|
38
|
+
'transition-transform',
|
|
39
|
+
'duration-500',
|
|
40
|
+
props.index !== undefined ? rotate[props.index] : '',
|
|
41
|
+
props.index !== undefined && props.active ? translate[props.index] : '',
|
|
42
|
+
props.className,
|
|
43
|
+
])
|
|
33
44
|
</script>
|
|
34
45
|
|
|
35
46
|
<template>
|
|
36
|
-
<div :class="
|
|
37
|
-
${rotate[index]} ${active ? `${translate[index]}` : ''} ${className || ''}`">
|
|
47
|
+
<div :class="cardClass">
|
|
38
48
|
<div class="card flex flex-col items-center justify-around">
|
|
39
49
|
<h3 class="max-w-[8rem] text-center text-xl font-bold text-heading text-balance">
|
|
40
50
|
{{ name }}
|
package/dist/theme.css
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* ==========================================================================
|
|
2
2
|
* CODECAVE Tailwind theme — @codecavepro/brand/theme.css
|
|
3
3
|
* --------------------------------------------------------------------------
|
|
4
|
-
* GENERATED from
|
|
4
|
+
* GENERATED from src/styles/theme.css — do not edit.
|
|
5
5
|
*
|
|
6
6
|
* Declares the names Tailwind needs in order to emit CODECAVE utility
|
|
7
7
|
* classes: bg-surface-primary, text-heading-lg, rounded-card and the rest.
|
package/dist/tokens.css
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* ==========================================================================
|
|
2
2
|
* CODECAVE design tokens — @codecavepro/brand/tokens.css
|
|
3
3
|
* --------------------------------------------------------------------------
|
|
4
|
-
* GENERATED from
|
|
4
|
+
* GENERATED from src/styles/colors_and_type.css — do not edit.
|
|
5
5
|
*
|
|
6
6
|
* The custom properties and nothing else: no @font-face, no rules for html,
|
|
7
7
|
* body, headings or links, no layout primitives, no components. Import this
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codecavepro/brand",
|
|
3
|
-
"version": "2.2
|
|
3
|
+
"version": "2.4.2",
|
|
4
4
|
"description": "CODECAVE design system — colour, typography and layout tokens as CSS custom properties and as a typed module.",
|
|
5
5
|
"license": "Unlicense",
|
|
6
6
|
"type": "module",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"./tokens.css": "./dist/tokens.css",
|
|
29
29
|
"./theme.css": "./dist/theme.css",
|
|
30
30
|
"./fonts.css": "./dist/fonts.css",
|
|
31
|
-
"./components/*": "./dist/src
|
|
31
|
+
"./components/*": "./dist/src/*",
|
|
32
32
|
"./assets/*": "./dist/src/assets/*",
|
|
33
33
|
"./helpers/*": "./dist/src/helpers/*",
|
|
34
34
|
"./lib/*": "./dist/src/lib/*",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|