Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 35 additions & 52 deletions src/components/link/CdrLink.vue
Original file line number Diff line number Diff line change
@@ -1,76 +1,59 @@
<script setup lang="ts">
import { useCssModule, computed } from 'vue';
import mapClasses from '../../utils/mapClasses';
import propValidator from '../../utils/propValidator';
import { useCssModule, computed, useAttrs } from 'vue';
import type { CdrLinkProps } from '../../types/interfaces';

/** Clickable text elements used for navigating to other pages or sections */
defineOptions({
name: 'CdrLink',
});
defineOptions({ name: 'CdrLink' });

const props = defineProps({
/**
* Sets valid HTML element tag
* @values a, button
*/
tag: {
type: String,
default: 'a',
},
/**
* Sets value for anchors href property. Requires tag prop value to be `a`.
*/
href: {
type: String,
default: '#',
},
/**
* Sets color and fill
*/
inheritColor: {
type: Boolean,
default: false,
},
/**
* Modifies the style variant for this component.
* @values standalone
*/
modifier: {
type: String,
default: '',
validator: (value: string) => propValidator(value, ['', 'standalone']),
},
/** @ignore */
target: String,
/** @ignore */
rel: String,
const props = withDefaults(defineProps<CdrLinkProps>(), {
tag: 'a',
href: '#',
inheritColor: false,
modifier: '',
});

const style = useCssModule();
const baseClass = 'cdr-link';
const computedHref = computed(() => (props.tag === 'a' ? props.href : null));
const attrs = useAttrs();

// Use baseClass for CSS Modules and for future-proofing versioned class names
const baseClass = style['cdr-link']; // always use the CSS module version

const computedHref = computed(() => (props.tag === 'a' ? props.href : undefined));
const computedRel = computed(() => {
if (props.target === '_blank') {
return props.rel || 'noopener noreferrer';
}
return props.rel;
});
const modifierClass = computed(() => props.modifier ? `${baseClass}--${props.modifier}` : '');
const inheritColorClass = computed(() => props.inheritColor ? 'cdr-link--inherit-color' : '');

const getModifierClasses = (modifier: string) => {
if (!modifier) return [];
return modifier
.split(' ')
.map((mod) => mod.trim())
.filter(Boolean)
.map((mod) => style[`cdr-link--${mod}`])
.filter(Boolean);
};
</script>

<template>
<component
:is="tag"
:class="mapClasses(style, baseClass, modifierClass, inheritColorClass)"
:target="target"
:rel="computedRel"
:is="props.tag"
:class="[
baseClass,
...getModifierClasses(props.modifier),
props.inheritColor && style['cdr-link--inherit-color'],
...(attrs.class ? [attrs.class] : [])
]"
:href="computedHref"
:rel="computedRel"
:target="props.target"
v-bind="attrs"
>
<!-- @slot Readable text of the link -->
<slot />
</component>
</template>

<style lang="scss" module src="./styles/CdrLink.module.scss">
</style>
<style lang="scss" module src="./styles/CdrLink.module.scss" />
70 changes: 70 additions & 0 deletions src/components/link/examples/demo/Resilience.vue
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,68 @@
</li>
</cdr-list>

<h3>Variant: Standalone</h3>
<cdr-link href="#foo" modifier="standalone">
Standalone variant link
</cdr-link>

<h3>Variant: Inherit Color</h3>
<div style="color: red;">
<cdr-link href="#foo" :inherit-color="true">
Inherit color (should be red)
</cdr-link>
</div>

<h3>Variant: Inherit Color (custom color context)</h3>
<p class="cdr-link-demo-red">
This is a paragraph with a
<cdr-link href="#foo" modifier="inherit-color">
red inherit-color link
</cdr-link>
inside.
</p>

<h3>Variant: Neutral</h3>
<cdr-link href="#foo" modifier="neutral">
Neutral variant link (should use primary text color)
</cdr-link>

<h3>Combined: Standalone + Inherit Color</h3>
<div style="color: green;">
<cdr-link href="#foo" modifier="standalone" inherit-color>
Standalone + inherit color (should be green, no underline by default)
</cdr-link>
</div>

<h3>Combined: Standalone + Inherit Color (custom color context)</h3>
<p class="cdr-link-demo-green">
This is a paragraph with a
<cdr-link href="#foo" modifier="standalone inherit-color">
green standalone + inherit-color link
</cdr-link>
inside.
</p>

<h3>Combined: Standalone + Neutral</h3>
<cdr-link href="#foo" modifier="standalone neutral">
Standalone + neutral (should use primary text color, no underline by default)
</cdr-link>

<h3>Edge: Button Tag</h3>
<cdr-link tag="button">
Rendered as button
</cdr-link>

<h3>Edge: Disabled Button</h3>
<cdr-link tag="button" :disabled="true">
Disabled button link
</cdr-link>

<h3>Edge: Custom rel and target</h3>
<cdr-link href="https://rei.com" target="_blank" rel="noopener">
External link with custom rel/target
</cdr-link>

</div>
</template>

Expand Down Expand Up @@ -183,4 +245,12 @@ export default {
p {
margin: 1rem 0!important;
}

.cdr-link-demo-red {
color: red;
}

.cdr-link-demo-green {
color: green;
}
</style>
48 changes: 48 additions & 0 deletions src/components/link/examples/demo/Standard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,47 @@
REI.com
</cdr-link>

<h3>Standalone + Neutral (Combined props)</h3>
<cdr-link
modifier="standalone neutral"
href="#bar"
data-backstop="cdr-link--standalone-neutral"
>
Standalone + Neutral
</cdr-link>

<h3>Standalone + Inherit Color (Combined props)</h3>
<div style="color: blue;">
<cdr-link
modifier="standalone"
href="#bar"
inherit-color
data-backstop="cdr-link--standalone-inherit"
>
Standalone + Inherit Color (should be blue)
</cdr-link>
</div>

<h3>Neutral Only (Prop usage)</h3>
<cdr-link
modifier="neutral"
href="#bar"
data-backstop="cdr-link--neutral"
>
Neutral Only
</cdr-link>

<h3>Inherit Color Only (Prop usage)</h3>
<div style="color: orange;">
<cdr-link
href="#bar"
inherit-color
data-backstop="cdr-link--inherit"
>
Inherit Color Only (should be orange)
</cdr-link>
</div>

<h3>Links, with icon</h3>

<cdr-list>
Expand Down Expand Up @@ -153,4 +194,11 @@ export default {
padding-right: 0;
}

/* Remove .cdr-link-demo-neutral and .cdr-link--neutral, as variants should be set via props */

/* Example: Inherit color using custom property override for legacy/demo only */
.cdr-link-demo-inherit {
--cdr-link-text-color: inherit;
--cdr-link-fill-color: inherit;
}
</style>
6 changes: 4 additions & 2 deletions src/components/link/styles/CdrLink.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
@include cdr-link-base-mixin;
}

/* Standalone
========== */
.cdr-link--standalone {
@include cdr-link-standalone-mixin;
}

.cdr-link--inherit-color {
@include cdr-link-inherit-color-mixin;
}

.cdr-link--neutral {
@include cdr-link-neutral-mixin;
}
85 changes: 45 additions & 40 deletions src/components/link/styles/vars/CdrLink.vars.scss
Original file line number Diff line number Diff line change
@@ -1,62 +1,67 @@
/**
* Base styles for CdrLink.
* Uses custom properties for easy theming and overrides.
* - Typography/layout: font, size, alignment, etc.
* - Color: uses --cdr-link-text-color and --cdr-link-fill-color for theming.
* - Decoration: uses --cdr-link-text-decoration for underline/none.
*/
@mixin cdr-link-base-mixin() {
font-family: inherit;
font-size: inherit;
line-height: inherit;
align-items: center;
background-color: transparent;
border: 0;
margin: 0;
font-family: var(--cdr-link-font-family, inherit);
font-size: var(--cdr-link-font-size, inherit);
line-height: var(--cdr-link-line-height, inherit);
align-items: var(--cdr-link-align-items, center);
background-color: var(--cdr-link-background-color, transparent);
border: var(--cdr-link-border, 0);
margin: var(--cdr-link-margin, 0);
cursor: var(--cdr-link-cursor, pointer);
display: var(--cdr-link-display, inline-flex);
outline: var(--cdr-link-outline, none);
padding: var(--cdr-link-padding, 0);
vertical-align: var(--cdr-link-vertical-align, top);

//ITEM_DOC: Color of the link text
color: var(--cdr-link-text-color, var(--cdr-color-text-link-rest, #{$cdr-color-text-link-rest}));

//ITEM_DOC: Color of the link fill
fill: var(--cdr-link-fill-color, var(--cdr-color-text-link-rest, #{$cdr-color-text-link-rest}));
cursor: pointer;
display: inline-flex;
outline: none;
padding: 0;
text-decoration: underline;
vertical-align: top;
// Themed color and decoration
color: var(--cdr-link-text-color, var(--cdr-color-text-link-rest, var(--cdr-color-text-link-rest, #{$cdr-color-text-link-rest})));
fill: var(--cdr-link-fill-color, var(--cdr-color-text-link-rest, var(--cdr-color-text-link-rest, #{$cdr-color-text-link-rest})));
text-decoration: var(--cdr-link-text-decoration, underline);

&:active,
&:focus {
//ITEM_DOC: Color of the link text when active or focused
color: var(--cdr-link-active-text-color-active, var(--cdr-color-text-link-active, #{$cdr-color-text-link-active}));
color: var(--cdr-link-active-text-color-active, var(--cdr-color-text-link-active, var(--cdr-color-text-link-active, #{$cdr-color-text-link-active})));
}

&:hover {
//ITEM_DOC: Color of the link text when hovered
color: var(--cdr-link-text-color-hover, var(--cdr-color-text-link-hover, #{$cdr-color-text-link-hover}));
text-decoration: none;
color: var(--cdr-link-text-color-hover, var(--cdr-color-text-link-hover, var(--cdr-color-text-link-hover, #{$cdr-color-text-link-hover})));
text-decoration: var(--cdr-link-text-decoration-hover, none);
}

&:focus {
outline: $default-outline;
outline: 2px solid var(--cdr-color-outline, Highlight);
outline-color: -webkit-focus-ring-color;
outline-offset: 0;
}
}

@mixin cdr-link-inherit-color-mixin() {
fill: inherit;
color: inherit;

&:active,
&:hover,
&:focus {
color: inherit;
fill: inherit;
}
// Inherit text and fill color from parent for the link and its icon content
color: inherit !important;
fill: inherit !important;
--cdr-link-text-color: inherit;
--cdr-link-fill-color: inherit ;
--cdr-link-text-color-hover: inherit;
--cdr-link-active-text-color-active: inherit ;
}

@mixin cdr-link-standalone-mixin() {
text-decoration: none;
// Standalone variant styles
// Only set text-decoration if not already set by another variant
--cdr-link-text-decoration: none;
--cdr-link-text-decoration-hover: underline;
}

&:hover,
&:active,
&:focus,
&:visited {
text-decoration: underline;
}
@mixin cdr-link-neutral-mixin() {
// Neutral variant: all states use primary text color
// Only set color variables if not already set by another variant
--cdr-link-text-color: var(--cdr-color-text-primary, #{$cdr-color-text-primary});
--cdr-link-text-color-hover: var(--cdr-color-text-primary, #{$cdr-color-text-primary});
--cdr-link-active-text-color-active: var(--cdr-color-text-primary, #{$cdr-color-text-primary});
}
Loading