Skip to content

chore(#158): allow classes to pass into link.vue if provided - #159

Merged
Decipher merged 1 commit into
developfrom
feature/158-link_classes
Dec 7, 2025
Merged

Decipher merged 1 commit into
developfrom
feature/158-link_classes

Conversation

@BrianGilbert

@BrianGilbert BrianGilbert commented Dec 4, 2025 •

Copy link
Copy Markdown
Member

Types of changes

  • Bug fix (a non-breaking change which fixes an issue)
  • New feature (a non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Description

Checklist:

  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have added tests to cover my changes (if not applicable, please state why)
  • All new and existing tests are passing.

Screenshots/Media:

Summary by CodeRabbit

  • New Features
    • Added an option for link components to opt out of the default base CSS class, allowing fully custom styling without the component's base styles.
    • When opted out, modifier classes for disabled and external links still apply and theme styles continue to be applied for consistent theming across variants.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Dec 4, 2025 •

Copy link
Copy Markdown

Walkthrough

Adds a boolean prop noBaseClasses to src/components/Link.vue and updates the classes computed property to omit the base ct-link class when noBaseClasses is true while retaining modifier and theme classes.

Changes

Cohort / File(s) Summary
Link component — prop & class computation
src/components/Link.vue
Adds noBaseClasses: Boolean prop (default false); updates classes computed to include 'ct-link' only when !noBaseClasses, and still include 'ct-link--disabled', 'ct-link--external', and [themeClass] as before.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Verify noBaseClasses prop name, type and default value.
  • Confirm classes computed output for combinations of noBaseClasses, disabled, and isExternal.
  • Ensure themeClass continues to be applied as expected.

Poem

🐇 I nudged a class, I gave a hop,
A base can hide — then show on top,
Modifiers dance, the theme stays bright,
A tiny change, a lighter sprite. 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title mentions allowing classes to pass into Link.vue, but the actual change only adds a noBaseClasses prop to conditionally exclude the 'ct-link' base class—it doesn't enable passing arbitrary classes into the component. Revise the title to accurately reflect the change, such as 'chore(#158): add noBaseClasses prop to Link.vue' or 'chore(#158): allow excluding base classes in Link.vue'.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/158-link_classes

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eacdbe6 and 64ff34c.

📒 Files selected for processing (1)
  • src/components/Link.vue (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build (16.x)
🔇 Additional comments (1)
src/components/Link.vue (1)

83-88: classes computed now correctly preserves theme/state while gating base class

This implementation looks good: noBaseClasses only controls the base 'ct-link' class, while ct-link--disabled, ct-link--external, and themeClass are always applied as appropriate, which addresses the earlier concerns about theming and accessibility state classes being dropped.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb4f875 and 0aedba3.

📒 Files selected for processing (1)
  • src/components/Link.vue (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build (16.x)
🔇 Additional comments (2)
src/components/Link.vue (2)

40-43: LGTM: Prop definition is correct.

The classes prop is properly defined with appropriate type and default value.


4-4: LGTM: Template binding correctly updated.

The template correctly binds to the new computedClasses computed property, assuming the logic within computedClasses is fixed per the previous comment.

Comment thread src/components/Link.vue Outdated
Comment on lines +83 to +103
computedClasses: ({ classes, disabled, isExternal, themeClass }) => {
const result = {
[themeClass]: true
};

if (classes) {
// Parse the classes string and add each class to the result
classes.split(" ").forEach(className => {
if (className.trim()) {
result[className.trim()] = true;
}
});
} else {
// Add default classes only if no classes provided
result["ct-link"] = true;
result["ct-link--disabled"] = disabled;
result["ct-link--external"] = isExternal;
}

return result;
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Custom classes replace defaults instead of merging.

The current logic completely omits default classes (ct-link, ct-link--disabled, ct-link--external) when custom classes are provided. This causes:

  1. Loss of base ct-link styling
  2. Disabled and external states are no longer reflected in CSS classes
  3. Component breaks visually and functionally

Additionally, an empty string "" is truthy in JavaScript, so passing classes="" would result in only themeClass being applied, losing all default classes.

Apply this diff to merge custom classes with defaults instead of replacing them:

-    computedClasses: ({ classes, disabled, isExternal, themeClass }) => {
-      const result = {
-        [themeClass]: true
-      };
-
-      if (classes) {
-        // Parse the classes string and add each class to the result
-        classes.split(" ").forEach(className => {
-          if (className.trim()) {
-            result[className.trim()] = true;
-          }
-        });
-      } else {
-        // Add default classes only if no classes provided
-        result["ct-link"] = true;
-        result["ct-link--disabled"] = disabled;
-        result["ct-link--external"] = isExternal;
-      }
-
-      return result;
-    },
+    computedClasses: ({ classes, disabled, isExternal, themeClass }) => {
+      const result = {
+        "ct-link": true,
+        "ct-link--disabled": disabled,
+        "ct-link--external": isExternal,
+        [themeClass]: true
+      };
+
+      // Merge custom classes if provided
+      if (classes && classes.trim()) {
+        classes.split(" ").forEach(className => {
+          if (className.trim()) {
+            result[className.trim()] = true;
+          }
+        });
+      }
+
+      return result;
+    },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
computedClasses: ({ classes, disabled, isExternal, themeClass }) => {
const result = {
[themeClass]: true
};
if (classes) {
// Parse the classes string and add each class to the result
classes.split(" ").forEach(className => {
if (className.trim()) {
result[className.trim()] = true;
}
});
} else {
// Add default classes only if no classes provided
result["ct-link"] = true;
result["ct-link--disabled"] = disabled;
result["ct-link--external"] = isExternal;
}
return result;
},
computedClasses: ({ classes, disabled, isExternal, themeClass }) => {
const result = {
"ct-link": true,
"ct-link--disabled": disabled,
"ct-link--external": isExternal,
[themeClass]: true
};
// Merge custom classes if provided
if (classes && classes.trim()) {
classes.split(" ").forEach(className => {
if (className.trim()) {
result[className.trim()] = true;
}
});
}
return result;
},
🤖 Prompt for AI Agents
In src/components/Link.vue around lines 83 to 103, the computedClasses function
currently replaces default classes when a classes string is provided and treats
an empty string as truthy; change it so default classes (ct-link,
ct-link--disabled, ct-link--external) are always set on the result, then parse
the classes string (guarding against empty/whitespace) and add each custom class
into the result; ensure disabled and isExternal still control their respective
class flags and that trimming/splitting handles multiple spaces without removing
defaults so custom classes are merged with, not replace, the defaults.

@BrianGilbert
BrianGilbert force-pushed the feature/158-link_classes branch from 0aedba3 to 9b5d5bd Compare December 4, 2025 22:42

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0aedba3 and 9b5d5bd.

📒 Files selected for processing (1)
  • src/components/Link.vue (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build (16.x)

Comment thread src/components/Link.vue Outdated
Comment on lines 83 to 88
classes: ({ disabled, isExternal, themeClass, noClasses }) => ({
'ct-link': !noClasses,
'ct-link--disabled': disabled && !noClasses,
'ct-link--external': isExternal && !noClasses,
[themeClass]: !noClasses
}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n src/components/Link.vue | head -150

Repository: Realityloop/civictheme-vue

Length of output: 2924


🏁 Script executed:

rg -n --type=vue --type=js --type=ts 'noClasses' -g '!src/components/Link.vue'

Repository: Realityloop/civictheme-vue

Length of output: 97


🏁 Script executed:

rg -n --type=vue --type=js --type=ts 'ct-link--disabled|ct-link--external'

Repository: Realityloop/civictheme-vue

Length of output: 97


🏁 Script executed:

rg 'noClasses' --type=js -i

Repository: Realityloop/civictheme-vue

Length of output: 463


🏁 Script executed:

cat -n src/mixins/theme.js | head -50

Repository: Realityloop/civictheme-vue

Length of output: 320


🏁 Script executed:

rg 'ct-link' --type=js --type=css -A 2 -B 2

Repository: Realityloop/civictheme-vue

Length of output: 5521


🏁 Script executed:

fd -e css -e scss | xargs cat | grep -A 5 'ct-link--disabled\|ct-link--external' | head -50

Repository: Realityloop/civictheme-vue

Length of output: 52


🏁 Script executed:

find . -name '*.css' -o -name '*.scss' | xargs grep -l 'ct-link'

Repository: Realityloop/civictheme-vue

Length of output: 54


🏁 Script executed:

git log --oneline -20 -- src/components/Link.vue

Repository: Realityloop/civictheme-vue

Length of output: 138


🏁 Script executed:

git show 9b5d5bd --stat

Repository: Realityloop/civictheme-vue

Length of output: 50382


🏁 Script executed:

git show 9b5d5bd

Repository: Realityloop/civictheme-vue

Length of output: 50384


🏁 Script executed:

git show 9b5d5bd -- src/components/Link.vue

Repository: Realityloop/civictheme-vue

Length of output: 2644


Don't remove theme and accessibility classes when noClasses is true.

When noClasses is true, the current implementation removes themeClass and the modifier classes (ct-link--disabled, ct-link--external). This is problematic:

  1. Theme removal: The themeClass (e.g., ct-theme-light) applies essential theming that controls link appearance across the application. Removing it breaks the design system.

  2. Accessibility: The modifier classes provide important visual state feedback for users. Removing ct-link--disabled and ct-link--external eliminates these indicators.

If the intent of noClasses is to remove only the base styling while preserving semantic structure, the computed property should keep the theme and state classes:

classes: ({ disabled, isExternal, themeClass, noClasses }) => ({
  'ct-link': !noClasses,
  'ct-link--disabled': disabled,
  'ct-link--external': isExternal,
  [themeClass]: true
})

This way noClasses still removes the base .ct-link class but preserves theming and accessibility markers.

🤖 Prompt for AI Agents
In src/components/Link.vue around lines 83 to 88, the classes computed property
currently drops themeClass and modifier classes when noClasses is true; update
the logic so noClasses only omits the base 'ct-link' class while always
including the themeClass and still adding state modifiers when applicable (i.e.,
keep 'ct-link--disabled' when disabled, 'ct-link--external' when isExternal, and
always include themeClass regardless of noClasses).

@BrianGilbert
BrianGilbert force-pushed the feature/158-link_classes branch from 9b5d5bd to 06abef0 Compare December 5, 2025 00:12

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b5d5bd and 06abef0.

📒 Files selected for processing (1)
  • src/components/Link.vue (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build (16.x)
🔇 Additional comments (1)
src/components/Link.vue (1)

4-4: Good: Enables external class merging via Vue's standard mechanism.

The template binding change from :class="classes" to :class="[classes, $attrs.class]" successfully achieves the PR objective. Using $attrs.class is the Vue-idiomatic way to allow external classes to be passed in and merged, rather than creating a custom classes prop. When consumers use <Link class="my-custom-class">, those classes will now be merged with the component's internal classes.

Comment thread src/components/Link.vue Outdated
Comment on lines +64 to +67
noClasses: {
type: Boolean,
default: false
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Suggest renaming noClasses for clarity.

The prop name noClasses is misleading because it doesn't remove all classes—it only omits the base ct-link class while preserving modifiers (ct-link--disabled, ct-link--external) and the theme class. Consider renaming to noBaseClass or excludeBaseClass to more accurately reflect its behavior.

Additionally, the use case for this prop isn't documented. Please add a comment or documentation explaining when developers should use this flag (e.g., when they want to provide completely custom styling while maintaining semantic state classes).

Apply this diff:

-    noClasses: {
+    noBaseClass: {
       type: Boolean,
       default: false
     },

And update the computed property accordingly:

-    classes: ({ disabled, isExternal, themeClass, noClasses }) => {
+    classes: ({ disabled, isExternal, themeClass, noBaseClass }) => {
       const cls = {}
-      if (!noClasses) cls['ct-link'] = true
+      if (!noBaseClass) cls['ct-link'] = true
       if (disabled) cls['ct-link--disabled'] = true
       if (isExternal) cls['ct-link--external'] = true
       if (themeClass) cls[themeClass] = true
       return cls
     },

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/components/Link.vue around lines 64 to 67, rename the prop noClasses to
noBaseClass (or excludeBaseClass) to accurately reflect that it only omits the
base "ct-link" class while keeping modifier and theme classes; update the prop
declaration name and default, update any usages and the computed class-building
property to check the new prop name, and add a brief inline comment above the
prop explaining the intended use (e.g., use when you want to omit only the base
ct-link class but retain semantic modifier/theme classes for state and
accessibility).

Comment thread src/components/Link.vue Outdated
Comment on lines +83 to +90
classes: ({ disabled, isExternal, themeClass, noClasses }) => {
const cls = {}
if (!noClasses) cls['ct-link'] = true
if (disabled) cls['ct-link--disabled'] = true
if (isExternal) cls['ct-link--external'] = true
if (themeClass) cls[themeClass] = true
return cls
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "*.vue" -o -name "*.js" | grep -i "link\|theme" | head -20

Repository: Realityloop/civictheme-vue

Length of output: 244


🏁 Script executed:

cat -n src/components/Link.vue | head -100

Repository: Realityloop/civictheme-vue

Length of output: 2822


🏁 Script executed:

fd -type f -name "*.js" | xargs rg -l "ThemeMixin\|themeClass" | head -10

Repository: Realityloop/civictheme-vue

Length of output: 240


🏁 Script executed:

cat -n src/mixins/theme.js

Repository: Realityloop/civictheme-vue

Length of output: 320


🏁 Script executed:

rg -n "themeClass" src/mixins/theme.js -A 5 -B 5

Repository: Realityloop/civictheme-vue

Length of output: 194


Remove redundant guard on themeClass.

The if (themeClass) check on line 88 is unnecessary. The ThemeMixin defines themeClass as a computed property that always returns a string: `ct-theme-${theme}`, where theme defaults to 'light'. This means themeClass is always a non-empty string and will never be falsy. Remove the conditional guard and directly assign the theme class:

cls[themeClass] = true
🤖 Prompt for AI Agents
In src/components/Link.vue around lines 83 to 90, the guard checking `if
(themeClass)` is redundant because ThemeMixin guarantees `themeClass` is always
a non-empty string; remove that conditional and always set `cls[themeClass] =
true` so the theme class is always applied, leaving the other guards intact.

@BrianGilbert
BrianGilbert force-pushed the feature/158-link_classes branch from 06abef0 to eacdbe6 Compare December 5, 2025 00:22
@BrianGilbert
BrianGilbert force-pushed the feature/158-link_classes branch from eacdbe6 to 64ff34c Compare December 7, 2025 22:29
@Decipher
Decipher merged commit 1a999a5 into develop Dec 7, 2025
1 check passed
@Decipher
Decipher deleted the feature/158-link_classes branch December 7, 2025 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants