srcdev-nuxt-components 9.5.2 → 9.5.4
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/.claude/skills/colour-scheme-disable.md +1 -1
- package/.claude/skills/component-compliance-checklist.md +214 -0
- package/.claude/skills/component-inline-action-button.md +2 -2
- package/.claude/skills/components/canvas-switcher.md +5 -1
- package/.claude/skills/components/carousel-flip.md +3 -3
- package/.claude/skills/components/clipped-panel.md +2 -2
- package/.claude/skills/components/dashboard-quad-grid.md +2 -2
- package/.claude/skills/components/dashboard-stats-grid.md +1 -1
- package/.claude/skills/components/deep-expanding-menu-classic.md +1 -1
- package/.claude/skills/components/deep-expanding-menu.md +1 -1
- package/.claude/skills/components/eyebrow-text.md +6 -4
- package/.claude/skills/components/google-reviews.md +193 -0
- package/.claude/skills/components/input-number.md +4 -0
- package/.claude/skills/components/input-otp.md +120 -0
- package/.claude/skills/components/section-parallax.md +1 -1
- package/.claude/skills/components/select-menu.md +9 -5
- package/.claude/skills/components/services-card.md +25 -2
- package/.claude/skills/composable-colour-scheme.md +2 -0
- package/.claude/skills/index.md +5 -1
- package/.claude/skills/new-app-scaffold.md +1 -1
- package/.claude/skills/storybook-add-story.md +5 -0
- package/.claude/skills/theming-colour-ramps.md +6 -33
- package/.claude/skills/theming-component-token-pattern.md +30 -0
- package/.claude/skills/theming-dark-mode.md +243 -0
- package/.claude/skills/theming-override-default.md +6 -7
- package/.claude/skills/theming-partial-override.md +11 -11
- package/.vscode/srcdev-component-eyebrow-text.code-snippets +4 -0
- package/.vscode/srcdev-component-google-reviews.code-snippets +78 -0
- package/.vscode/srcdev-component-input-otp.code-snippets +47 -0
- package/.vscode/srcdev-component-select-menu.code-snippets +13 -0
- package/.vscode/srcdev-component-services-card.code-snippets +38 -7
- package/README.md +81 -41
- package/app/assets/styles/setup/01.config/_head.css +0 -9
- package/app/assets/styles/setup/03.theming/_default.css +29 -29
- package/app/assets/styles/setup/03.theming/_error.css +3 -3
- package/app/assets/styles/setup/03.theming/_theme-slots.css +13 -16
- package/app/assets/styles/setup/a11y/_variables.css +2 -2
- package/app/components/01.atoms/page-row/stories/PageRow.stories.ts +47 -13
- package/app/components/01.atoms/text-blocks/eyebrow-text/CONSUMER-STYLING.md +5 -1
- package/app/components/01.atoms/text-blocks/eyebrow-text/EyebrowText.vue +4 -1
- package/app/components/02.molecules/action-menu/stories/ActionMenu.stories.ts +7 -7
- package/app/components/02.molecules/select-menu/CONSUMER-STYLING.md +10 -0
- package/app/components/02.molecules/select-menu/SelectMenu.vue +15 -1
- package/app/components/02.molecules/select-menu/stories/SelectMenu.stories.ts +35 -0
- package/app/components/02.molecules/select-menu/tests/SelectMenu.spec.ts +9 -0
- package/app/components/02.molecules/select-menu/tests/__snapshots__/SelectMenu.spec.ts.snap +3 -3
- package/app/components/03.organisms/google-reviews/CONSUMER-STYLING.md +110 -0
- package/app/components/03.organisms/google-reviews/GoogleReviewCard.vue +161 -0
- package/app/components/03.organisms/google-reviews/GoogleReviews.vue +275 -0
- package/app/components/03.organisms/google-reviews/stories/GoogleReviews.stories.ts +323 -0
- package/app/components/03.organisms/google-reviews/tests/GoogleReviewCard.spec.ts +104 -0
- package/app/components/03.organisms/google-reviews/tests/GoogleReviews.spec.ts +232 -0
- package/app/components/03.organisms/google-reviews/tests/__snapshots__/GoogleReviewCard.spec.ts.snap +9 -0
- package/app/components/03.organisms/google-reviews/tests/__snapshots__/GoogleReviews.spec.ts.snap +43 -0
- package/app/components/03.organisms/services/services-card/CONSUMER-STYLING.md +28 -3
- package/app/components/03.organisms/services/services-card/ServicesCard.vue +111 -18
- package/app/components/03.organisms/services/services-card/stories/ServicesCard.stories.ts +122 -0
- package/app/components/03.organisms/services/services-card/tests/__snapshots__/ServicesCard.spec.ts.snap +4 -1
- package/app/components/03.organisms/services/services-grids/stories/ServicesCardGrid.stories.ts +100 -19
- package/app/components/03.organisms/services/services-grids/tests/__snapshots__/ServicesCardGrid.spec.ts.snap +12 -3
- package/app/components/05.forms/input-number/CONSUMER-STYLING.md +1 -1
- package/app/components/05.forms/input-number/InputNumber.vue +2 -2
- package/app/components/05.forms/input-otp/CONSUMER-STYLING.md +59 -0
- package/app/components/05.forms/input-otp/InputOtp.vue +254 -0
- package/app/components/05.forms/input-otp/InputOtpField.vue +106 -0
- package/app/components/05.forms/input-otp/stories/InputOtp.stories.ts +100 -0
- package/app/components/05.forms/input-otp/stories/InputOtpField.stories.ts +163 -0
- package/app/components/05.forms/input-otp/tests/InputOtp.spec.ts +275 -0
- package/app/components/05.forms/input-otp/tests/InputOtpField.spec.ts +76 -0
- package/app/components/05.forms/patterns/stories/MigratedFieldsForm.stories.ts +151 -33
- package/app/composables/tests/useAriaDescribedById.spec.ts +5 -5
- package/app/composables/useGoogleReviews.ts +4 -0
- package/app/types/components/google-reviews.d.ts +18 -0
- package/app/types/components/index.ts +1 -0
- package/nuxt.config.ts +9 -9
- package/package.json +9 -8
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component :is="tag" class="google-review-card" :class="elementClasses">
|
|
3
|
+
<div class="google-review-card-author">
|
|
4
|
+
<DisplayAvatar :src="review.authorPhotoUri" :text="initials" size="md" aria-hidden="true" />
|
|
5
|
+
<a v-if="review.authorUri" :id="authorId" :href="review.authorUri" class="google-review-card-author-name">
|
|
6
|
+
{{ review.authorName }}
|
|
7
|
+
</a>
|
|
8
|
+
<span v-else :id="authorId" class="google-review-card-author-name">{{ review.authorName }}</span>
|
|
9
|
+
<time v-if="review.relativeTime" :datetime="review.publishTime || undefined" class="google-review-card-time">
|
|
10
|
+
{{ review.relativeTime }}
|
|
11
|
+
</time>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div class="google-review-card-rating">
|
|
15
|
+
<span class="google-review-card-stars" :style="{ '--_rating': review.rating }" aria-hidden="true">★★★★★</span>
|
|
16
|
+
<span class="sr-only">{{ formattedRatingLabel }}</span>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<p v-if="review.text" class="google-review-card-text">{{ review.text }}</p>
|
|
20
|
+
|
|
21
|
+
<a v-if="review.reviewUri" :href="review.reviewUri" :aria-describedby="authorId" class="google-review-card-link">
|
|
22
|
+
{{ readMoreLabel }}
|
|
23
|
+
</a>
|
|
24
|
+
</component>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<script setup lang="ts">
|
|
28
|
+
import type { GoogleReview } from "~/types/components";
|
|
29
|
+
|
|
30
|
+
interface Props {
|
|
31
|
+
review: GoogleReview;
|
|
32
|
+
tag?: "article" | "div";
|
|
33
|
+
ratingLabel?: string;
|
|
34
|
+
readMoreLabel?: string;
|
|
35
|
+
styleClassPassthrough?: string | string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
39
|
+
tag: "article",
|
|
40
|
+
ratingLabel: "Rated {rating} out of 5",
|
|
41
|
+
readMoreLabel: "Read on Google",
|
|
42
|
+
styleClassPassthrough: () => [],
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const authorId = useId();
|
|
46
|
+
const initials = computed(() =>
|
|
47
|
+
props.review.authorName
|
|
48
|
+
.split(/\s+/)
|
|
49
|
+
.filter(Boolean)
|
|
50
|
+
.slice(0, 2)
|
|
51
|
+
.map((word) => Array.from(word)[0]!.toUpperCase())
|
|
52
|
+
.join("")
|
|
53
|
+
);
|
|
54
|
+
const formattedRatingLabel = computed(() => props.ratingLabel.replace("{rating}", String(props.review.rating)));
|
|
55
|
+
|
|
56
|
+
const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
57
|
+
|
|
58
|
+
watch(
|
|
59
|
+
() => props.styleClassPassthrough,
|
|
60
|
+
() => {
|
|
61
|
+
resetElementClasses(props.styleClassPassthrough);
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<style lang="css">
|
|
67
|
+
@layer components {
|
|
68
|
+
.google-review-card {
|
|
69
|
+
display: grid;
|
|
70
|
+
grid-template-rows: auto auto auto 1fr auto;
|
|
71
|
+
align-items: start;
|
|
72
|
+
block-size: 100%;
|
|
73
|
+
padding: var(--google-reviews-card-padding, 2rem);
|
|
74
|
+
border: var(--google-reviews-card-border-width, 1px) solid var(--google-reviews-card-border-colour, transparent);
|
|
75
|
+
border-radius: var(--google-reviews-card-border-radius, 0.8rem);
|
|
76
|
+
background-color: var(--google-reviews-card-surface, var(--theme-surface-subtle));
|
|
77
|
+
color: var(--google-reviews-card-text-colour, var(--theme-text));
|
|
78
|
+
|
|
79
|
+
/* Name and date take a row each so both line up across a GoogleReviews row */
|
|
80
|
+
.google-review-card-author {
|
|
81
|
+
grid-row: 1 / span 2;
|
|
82
|
+
display: grid;
|
|
83
|
+
grid-template-columns: auto minmax(0, 1fr);
|
|
84
|
+
grid-template-rows: subgrid;
|
|
85
|
+
align-items: start;
|
|
86
|
+
column-gap: 1.2rem;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.google-review-card-author .display-avatar {
|
|
90
|
+
--display-avatar-background: var(--google-reviews-avatar-surface, var(--theme-surface));
|
|
91
|
+
--display-avatar-text-colour: var(--google-reviews-avatar-text-colour, var(--theme-on-surface));
|
|
92
|
+
|
|
93
|
+
grid-column: 1;
|
|
94
|
+
grid-row: 1 / -1;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.google-review-card-author-name {
|
|
98
|
+
grid-column: 2;
|
|
99
|
+
grid-row: 1;
|
|
100
|
+
display: -webkit-box;
|
|
101
|
+
-webkit-box-orient: vertical;
|
|
102
|
+
overflow: hidden;
|
|
103
|
+
-webkit-line-clamp: var(--google-reviews-author-line-clamp, 2);
|
|
104
|
+
line-clamp: var(--google-reviews-author-line-clamp, 2);
|
|
105
|
+
font-weight: var(--google-reviews-author-font-weight, 600);
|
|
106
|
+
color: inherit;
|
|
107
|
+
overflow-wrap: anywhere;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.google-review-card-time {
|
|
111
|
+
grid-column: 2;
|
|
112
|
+
grid-row: 2;
|
|
113
|
+
font-size: var(--google-reviews-meta-font-size, 1.4rem);
|
|
114
|
+
color: var(--google-reviews-meta-text-colour, inherit);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.google-review-card-rating {
|
|
118
|
+
grid-row: 3;
|
|
119
|
+
margin-block-start: var(--google-reviews-card-gap, 1.2rem);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.google-review-card-stars {
|
|
123
|
+
--_fill: clamp(0%, var(--_rating, 0) / 5 * 100%, 100%);
|
|
124
|
+
|
|
125
|
+
font-size: var(--google-reviews-star-size, 1.6rem);
|
|
126
|
+
letter-spacing: 0.1em;
|
|
127
|
+
background: linear-gradient(
|
|
128
|
+
90deg,
|
|
129
|
+
var(--google-reviews-star-colour, var(--amber-05)) var(--_fill),
|
|
130
|
+
var(--google-reviews-star-empty-colour, var(--slate-03)) var(--_fill)
|
|
131
|
+
);
|
|
132
|
+
-webkit-background-clip: text;
|
|
133
|
+
background-clip: text;
|
|
134
|
+
color: transparent;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.google-review-card-text {
|
|
138
|
+
grid-row: 4;
|
|
139
|
+
margin-block-start: var(--google-reviews-card-gap, 1.2rem);
|
|
140
|
+
margin-inline: 0;
|
|
141
|
+
margin-block-end: 0;
|
|
142
|
+
white-space: pre-line;
|
|
143
|
+
overflow-wrap: anywhere;
|
|
144
|
+
line-height: var(--google-reviews-text-line-height, 1.5);
|
|
145
|
+
display: -webkit-box;
|
|
146
|
+
-webkit-box-orient: vertical;
|
|
147
|
+
overflow: hidden;
|
|
148
|
+
-webkit-line-clamp: var(--google-reviews-text-line-clamp, 5);
|
|
149
|
+
line-clamp: var(--google-reviews-text-line-clamp, 5);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.google-review-card-link {
|
|
153
|
+
grid-row: 5;
|
|
154
|
+
margin-block-start: var(--google-reviews-card-gap, 1.2rem);
|
|
155
|
+
justify-self: start;
|
|
156
|
+
color: var(--google-reviews-link-colour, inherit);
|
|
157
|
+
font-size: var(--google-reviews-meta-font-size, 1.4rem);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
</style>
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component
|
|
3
|
+
:is="tag"
|
|
4
|
+
v-if="filteredReviews.length"
|
|
5
|
+
class="google-reviews"
|
|
6
|
+
:class="elementClasses"
|
|
7
|
+
:aria-labelledby="hasHeading() ? headingId : undefined"
|
|
8
|
+
:aria-label="hasHeading() ? undefined : ariaLabel"
|
|
9
|
+
>
|
|
10
|
+
<div v-if="hasHeading() || showSummary" class="google-reviews-header">
|
|
11
|
+
<slot name="heading" :heading-id="headingId"></slot>
|
|
12
|
+
<div v-if="showSummary && data" class="google-reviews-summary">
|
|
13
|
+
<span class="google-reviews-summary-rating" aria-hidden="true">{{ data.rating.toFixed(1) }}</span>
|
|
14
|
+
<span class="google-reviews-stars" :style="{ '--_rating': data.rating }" aria-hidden="true">★★★★★</span>
|
|
15
|
+
<span class="sr-only">{{ formatLabel(ratingLabel, data.rating.toFixed(1)) }}</span>
|
|
16
|
+
<a v-if="data.mapsUri" :href="data.mapsUri" class="google-reviews-summary-link">
|
|
17
|
+
{{ formatLabel(totalLabel, data.totalReviews) }}
|
|
18
|
+
</a>
|
|
19
|
+
<span v-else class="google-reviews-summary-link">{{ formatLabel(totalLabel, data.totalReviews) }}</span>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<p v-if="minRating > 0" class="google-reviews-filter-notice">{{ formatLabel(filterNotice, minRating) }}</p>
|
|
24
|
+
|
|
25
|
+
<ul ref="listRef" class="google-reviews-list" tabindex="0" @scroll.passive="updateScrollState">
|
|
26
|
+
<li v-for="(review, index) in filteredReviews" :key="review.reviewUri ?? index" class="google-reviews-item">
|
|
27
|
+
<slot name="card" :review="review">
|
|
28
|
+
<GoogleReviewCard :review="review" :rating-label="ratingLabel" :read-more-label="readMoreLabel" />
|
|
29
|
+
</slot>
|
|
30
|
+
</li>
|
|
31
|
+
</ul>
|
|
32
|
+
|
|
33
|
+
<div class="google-reviews-footer">
|
|
34
|
+
<div v-if="isOverflowing" class="google-reviews-controls">
|
|
35
|
+
<InputButton
|
|
36
|
+
variant="secondary"
|
|
37
|
+
:button-text="prevLabel"
|
|
38
|
+
:readonly="atStart"
|
|
39
|
+
class="google-reviews-prev"
|
|
40
|
+
@click="scrollByCard(-1)"
|
|
41
|
+
>
|
|
42
|
+
<template #iconOnly>
|
|
43
|
+
<Icon :name="prevIcon" class="icon" />
|
|
44
|
+
</template>
|
|
45
|
+
</InputButton>
|
|
46
|
+
<InputButton
|
|
47
|
+
variant="secondary"
|
|
48
|
+
:button-text="nextLabel"
|
|
49
|
+
:readonly="atEnd"
|
|
50
|
+
class="google-reviews-next"
|
|
51
|
+
@click="scrollByCard(1)"
|
|
52
|
+
>
|
|
53
|
+
<template #iconOnly>
|
|
54
|
+
<Icon :name="nextIcon" class="icon" />
|
|
55
|
+
</template>
|
|
56
|
+
</InputButton>
|
|
57
|
+
</div>
|
|
58
|
+
<p class="google-reviews-attribution">{{ attributionText }}</p>
|
|
59
|
+
</div>
|
|
60
|
+
</component>
|
|
61
|
+
</template>
|
|
62
|
+
|
|
63
|
+
<script setup lang="ts">
|
|
64
|
+
import type { GoogleReviewsData } from "~/types/components";
|
|
65
|
+
|
|
66
|
+
interface Props {
|
|
67
|
+
data: GoogleReviewsData | null;
|
|
68
|
+
minRating?: number;
|
|
69
|
+
showSummary?: boolean;
|
|
70
|
+
tag?: "section" | "div";
|
|
71
|
+
ariaLabel?: string;
|
|
72
|
+
prevLabel?: string;
|
|
73
|
+
nextLabel?: string;
|
|
74
|
+
prevIcon?: string;
|
|
75
|
+
nextIcon?: string;
|
|
76
|
+
ratingLabel?: string;
|
|
77
|
+
totalLabel?: string;
|
|
78
|
+
readMoreLabel?: string;
|
|
79
|
+
filterNotice?: string;
|
|
80
|
+
attributionText?: string;
|
|
81
|
+
styleClassPassthrough?: string | string[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
85
|
+
minRating: 0,
|
|
86
|
+
showSummary: true,
|
|
87
|
+
tag: "section",
|
|
88
|
+
ariaLabel: "Google reviews",
|
|
89
|
+
prevLabel: "Previous reviews",
|
|
90
|
+
nextLabel: "Next reviews",
|
|
91
|
+
prevIcon: "ic:outline-keyboard-arrow-left",
|
|
92
|
+
nextIcon: "ic:outline-keyboard-arrow-right",
|
|
93
|
+
ratingLabel: "Rated {rating} out of 5",
|
|
94
|
+
totalLabel: "{count} reviews on Google",
|
|
95
|
+
readMoreLabel: "Read on Google",
|
|
96
|
+
filterNotice: "Showing reviews rated {rating} stars and above, in Google's order.",
|
|
97
|
+
attributionText: "Reviews from Google Maps",
|
|
98
|
+
styleClassPassthrough: () => [],
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const slots = useSlots();
|
|
102
|
+
const headingId = useId();
|
|
103
|
+
const hasHeading = () => Boolean(slots.heading);
|
|
104
|
+
|
|
105
|
+
const formatLabel = (label: string, value: string | number) =>
|
|
106
|
+
label.replace("{rating}", String(value)).replace("{count}", String(value));
|
|
107
|
+
|
|
108
|
+
const filteredReviews = computed(() => (props.data?.reviews ?? []).filter((review) => review.rating >= props.minRating));
|
|
109
|
+
|
|
110
|
+
const listRef = ref<HTMLUListElement | null>(null);
|
|
111
|
+
const atStart = ref(true);
|
|
112
|
+
const atEnd = ref(true);
|
|
113
|
+
const isOverflowing = ref(false);
|
|
114
|
+
|
|
115
|
+
const updateScrollState = () => {
|
|
116
|
+
const list = listRef.value;
|
|
117
|
+
if (!list) return;
|
|
118
|
+
const offset = Math.abs(list.scrollLeft);
|
|
119
|
+
isOverflowing.value = list.scrollWidth > list.clientWidth + 1;
|
|
120
|
+
atStart.value = offset <= 1;
|
|
121
|
+
atEnd.value = offset + list.clientWidth >= list.scrollWidth - 1;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const scrollByCard = (direction: 1 | -1) => {
|
|
125
|
+
const list = listRef.value;
|
|
126
|
+
const item = list?.querySelector<HTMLElement>(".google-reviews-item");
|
|
127
|
+
if (!list || !item) return;
|
|
128
|
+
const styles = getComputedStyle(list);
|
|
129
|
+
const step = item.offsetWidth + (parseFloat(styles.columnGap) || 0);
|
|
130
|
+
const sign = styles.direction === "rtl" ? -1 : 1;
|
|
131
|
+
list.scrollBy({ left: direction * step * sign });
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
let resizeObserver: ResizeObserver | null = null;
|
|
135
|
+
|
|
136
|
+
onMounted(() => {
|
|
137
|
+
updateScrollState();
|
|
138
|
+
resizeObserver = new ResizeObserver(updateScrollState);
|
|
139
|
+
if (listRef.value) resizeObserver.observe(listRef.value);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
onBeforeUnmount(() => resizeObserver?.disconnect());
|
|
143
|
+
|
|
144
|
+
watch(filteredReviews, async () => {
|
|
145
|
+
await nextTick();
|
|
146
|
+
if (listRef.value) {
|
|
147
|
+
resizeObserver?.disconnect();
|
|
148
|
+
resizeObserver?.observe(listRef.value);
|
|
149
|
+
}
|
|
150
|
+
updateScrollState();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const { elementClasses, resetElementClasses } = useStyleClassPassthrough(props.styleClassPassthrough);
|
|
154
|
+
|
|
155
|
+
watch(
|
|
156
|
+
() => props.styleClassPassthrough,
|
|
157
|
+
() => {
|
|
158
|
+
resetElementClasses(props.styleClassPassthrough);
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
</script>
|
|
162
|
+
|
|
163
|
+
<style lang="css">
|
|
164
|
+
@layer components {
|
|
165
|
+
.google-reviews {
|
|
166
|
+
display: grid;
|
|
167
|
+
gap: var(--google-reviews-gap, 1.6rem);
|
|
168
|
+
min-inline-size: 0;
|
|
169
|
+
|
|
170
|
+
.google-reviews-header {
|
|
171
|
+
display: grid;
|
|
172
|
+
gap: 0.8rem;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.google-reviews-summary {
|
|
176
|
+
display: flex;
|
|
177
|
+
flex-wrap: wrap;
|
|
178
|
+
align-items: center;
|
|
179
|
+
gap: 0.8rem;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.google-reviews-summary-rating {
|
|
183
|
+
font-size: var(--google-reviews-summary-rating-font-size, 2.4rem);
|
|
184
|
+
font-weight: 700;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.google-reviews-stars {
|
|
188
|
+
--_fill: clamp(0%, var(--_rating, 0) / 5 * 100%, 100%);
|
|
189
|
+
|
|
190
|
+
font-size: var(--google-reviews-star-size, 1.6rem);
|
|
191
|
+
letter-spacing: 0.1em;
|
|
192
|
+
background: linear-gradient(
|
|
193
|
+
90deg,
|
|
194
|
+
var(--google-reviews-star-colour, var(--amber-05)) var(--_fill),
|
|
195
|
+
var(--google-reviews-star-empty-colour, var(--slate-03)) var(--_fill)
|
|
196
|
+
);
|
|
197
|
+
-webkit-background-clip: text;
|
|
198
|
+
background-clip: text;
|
|
199
|
+
color: transparent;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.google-reviews-summary-link {
|
|
203
|
+
color: inherit;
|
|
204
|
+
overflow-wrap: anywhere;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.google-reviews-filter-notice,
|
|
208
|
+
.google-reviews-attribution {
|
|
209
|
+
margin: 0;
|
|
210
|
+
overflow-wrap: anywhere;
|
|
211
|
+
font-size: var(--google-reviews-meta-font-size, 1.4rem);
|
|
212
|
+
color: var(--google-reviews-meta-text-colour, inherit);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.google-reviews-list {
|
|
216
|
+
display: grid;
|
|
217
|
+
grid-auto-flow: column;
|
|
218
|
+
grid-auto-columns: var(--google-reviews-card-width, min(32rem, 85%));
|
|
219
|
+
grid-template-rows: repeat(5, auto);
|
|
220
|
+
column-gap: var(--google-reviews-gap, 1.6rem);
|
|
221
|
+
row-gap: 0;
|
|
222
|
+
margin: 0;
|
|
223
|
+
padding: 0 0 0.8rem;
|
|
224
|
+
list-style: none;
|
|
225
|
+
overflow-x: auto;
|
|
226
|
+
overscroll-behavior-inline: contain;
|
|
227
|
+
scrollbar-width: var(--google-reviews-scrollbar-width, thin);
|
|
228
|
+
scrollbar-color: var(--google-reviews-scrollbar-thumb-colour, var(--slate-04))
|
|
229
|
+
var(--google-reviews-scrollbar-track-colour, transparent);
|
|
230
|
+
scroll-snap-type: inline mandatory;
|
|
231
|
+
scroll-padding-inline: var(--google-reviews-scroll-padding, 0);
|
|
232
|
+
scroll-behavior: smooth;
|
|
233
|
+
|
|
234
|
+
&:focus-visible {
|
|
235
|
+
outline: var(--google-reviews-focus-outline-width, 2px) solid var(--google-reviews-focus-outline-colour, var(--theme-border-focus));
|
|
236
|
+
outline-offset: 2px;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
@media (prefers-reduced-motion: reduce) {
|
|
240
|
+
scroll-behavior: auto;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/* Cards share the list's five rows (name, date, rating, text, link) so each section lines up across the row */
|
|
245
|
+
.google-reviews-item {
|
|
246
|
+
display: grid;
|
|
247
|
+
grid-row: span 5;
|
|
248
|
+
grid-template-rows: subgrid;
|
|
249
|
+
scroll-snap-align: start;
|
|
250
|
+
min-inline-size: 0;
|
|
251
|
+
|
|
252
|
+
> * {
|
|
253
|
+
grid-row: 1 / -1;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
> .google-review-card {
|
|
257
|
+
grid-template-rows: subgrid;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.google-reviews-footer {
|
|
262
|
+
display: flex;
|
|
263
|
+
flex-wrap: wrap;
|
|
264
|
+
align-items: center;
|
|
265
|
+
justify-content: space-between;
|
|
266
|
+
gap: 1.2rem;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.google-reviews-controls {
|
|
270
|
+
display: flex;
|
|
271
|
+
gap: 0.8rem;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
</style>
|