diff --git a/addons/rose/addon/components/rose/card/link.js b/addons/rose/addon/components/rose/card/link.js
new file mode 100644
index 000000000..162a08a0e
--- /dev/null
+++ b/addons/rose/addon/components/rose/card/link.js
@@ -0,0 +1,7 @@
+import Component from '@ember/component';
+import layout from '../../../templates/components/rose/card/link';
+
+export default Component.extend({
+ layout,
+ tagName: '',
+});
diff --git a/addons/rose/addon/components/rose/card/link.stories.mdx b/addons/rose/addon/components/rose/card/link.stories.mdx
new file mode 100644
index 000000000..571bfb28a
--- /dev/null
+++ b/addons/rose/addon/components/rose/card/link.stories.mdx
@@ -0,0 +1,60 @@
+import { Meta, Story, Preview } from '@storybook/addon-docs/blocks';
+import { hbs } from 'ember-cli-htmlbars';
+import { text } from '@storybook/addon-knobs';
+
+
+
+# Card Link
+
+Card link component wraps content with `LinkTo` component. Supports title and description.
+
+```html
+
+```
+
+
+ {{
+ template: hbs`
+
+ `,
+ context: {
+ title: text('title', 'Card title'),
+ description: text('description', 'Card description'),
+ },
+ }}
+
+
+`@route` defaults to 'index' when not supplied.
+
+`@title` and `@description` are optional. Card expands to fit content.
+
+```html
+
+```
+
+ {{
+ template: hbs`
+
+ `,
+ context: {
+ title: text('title', 'Card title'),
+ },
+ }}
+
+
+```html
+
+```
+
+ {{
+ template: hbs`
+
+ `,
+ context: {
+ description: text('description', 'Card description'),
+ },
+ }}
+
diff --git a/addons/rose/addon/styles/rose/components/_index.scss b/addons/rose/addon/styles/rose/components/_index.scss
index e6d3eefd1..bbd2ebcda 100644
--- a/addons/rose/addon/styles/rose/components/_index.scss
+++ b/addons/rose/addon/styles/rose/components/_index.scss
@@ -3,3 +3,4 @@
@import 'header';
@import 'layout';
@import 'notification';
+@import 'card';
diff --git a/addons/rose/addon/styles/rose/components/card/_index.scss b/addons/rose/addon/styles/rose/components/card/_index.scss
new file mode 100644
index 000000000..f641f2e2d
--- /dev/null
+++ b/addons/rose/addon/styles/rose/components/card/_index.scss
@@ -0,0 +1 @@
+@import 'link';
diff --git a/addons/rose/addon/styles/rose/components/card/_link.scss b/addons/rose/addon/styles/rose/components/card/_link.scss
new file mode 100644
index 000000000..949734531
--- /dev/null
+++ b/addons/rose/addon/styles/rose/components/card/_link.scss
@@ -0,0 +1,62 @@
+@use '../../variables/sizing';
+@use '../../utilities/type';
+
+.rose-card-link {
+ @include type.type(s);
+ border: 1px solid var(--ui-gray-subtler-2);
+ border-radius: sizing.rems(xxxs);
+ color: var(--ui-gray);
+ display: inline-flex;
+ flex-direction: column;
+
+ a {
+ text-decoration: none;
+ margin: 0;
+ padding: 0;
+ display: flex;
+ justify-content: space-between;
+ flex-direction: column;
+ height: 100%;
+ padding: sizing.rems(s);
+ // TODO: Figure out better method of defining card dimensions
+ min-height: sizing.rems(m) * 6; //6 rems
+ width: sizing.rems(m) * 12; //12 rems
+ }
+
+ &:hover {
+ border-color: var(--ui-gray);
+ background-color: var(--ui-gray-subtler-5);
+ }
+
+ &:active {
+ background-color: var(--ui-gray-subtler-4);
+ }
+
+ &:focus-within {
+ border-color: var(--info);
+ }
+
+ .rose-card-link-title {
+ $truncate-line-count: 3;
+ @include type.type(m, semibold);
+ color: var(--stark);
+ margin: 0;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: $truncate-line-count;
+ overflow: hidden;
+ }
+
+ .rose-card-link-subtitle {
+ color: var(--ui-gray);
+ margin: 0;
+ }
+
+ .rose-card-link-description {
+ margin: auto 0 0;
+ color: var(--ui-gray);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+}
diff --git a/addons/rose/addon/templates/components/rose/card/link.hbs b/addons/rose/addon/templates/components/rose/card/link.hbs
new file mode 100644
index 000000000..7f003a4a6
--- /dev/null
+++ b/addons/rose/addon/templates/components/rose/card/link.hbs
@@ -0,0 +1,21 @@
+
+
+ {{#if @title}}
+ {{@title}}
+ {{/if}}
+
+ {{#if @subtitle}}
+ {{@subtitle}}
+ {{/if}}
+
+ {{#if @description}}
+ {{@description}}
+ {{/if}}
+
+
+ {{yield}}
+
diff --git a/addons/rose/app/components/rose/card/link.js b/addons/rose/app/components/rose/card/link.js
new file mode 100644
index 000000000..350404c2b
--- /dev/null
+++ b/addons/rose/app/components/rose/card/link.js
@@ -0,0 +1 @@
+export { default } from 'rose/components/rose/card/link';
diff --git a/addons/rose/tests/dummy/app/templates/index.hbs b/addons/rose/tests/dummy/app/templates/index.hbs
index 12fac556c..3b791ba07 100644
--- a/addons/rose/tests/dummy/app/templates/index.hbs
+++ b/addons/rose/tests/dummy/app/templates/index.hbs
@@ -181,3 +181,7 @@ Dummy app index template
@submitText="Save"
@cancelText="Cancel" />
+
+
+
+
diff --git a/addons/rose/tests/integration/components/rose/card/link-test.js b/addons/rose/tests/integration/components/rose/card/link-test.js
new file mode 100644
index 000000000..78a02af4b
--- /dev/null
+++ b/addons/rose/tests/integration/components/rose/card/link-test.js
@@ -0,0 +1,54 @@
+import { module, test } from 'qunit';
+import { setupRenderingTest } from 'ember-qunit';
+import { render, find } from '@ember/test-helpers';
+import hbs from 'htmlbars-inline-precompile';
+
+module('Integration | Component | rose/card/link', function (hooks) {
+ setupRenderingTest(hooks);
+
+ test('it renders', async function (assert) {
+ await render(hbs``);
+ assert.ok(find('article'));
+ assert.ok(find('.rose-card-link'));
+ assert.notOk(find('.rose-card-link-title'));
+ assert.notOk(find('.rose-card-link-subtitle'));
+ assert.notOk(find('.rose-card-link-description'));
+ });
+
+ test('it renders with @title', async function (assert) {
+ await render(hbs``);
+ assert.equal(find('a').title, 'card title');
+ assert.equal(
+ find('.rose-card-link-title').textContent.trim(),
+ 'card title'
+ );
+ });
+
+ test('it renders with @subtitle', async function (assert) {
+ await render(hbs``);
+ assert.equal(
+ find('.rose-card-link-subtitle').textContent.trim(),
+ 'card subtitle'
+ );
+ });
+
+ test('it renders with @description', async function (assert) {
+ await render(hbs``);
+ assert.equal(
+ find('.rose-card-link-description').textContent.trim(),
+ 'card description'
+ );
+ });
+
+ test('it renders with attributes', async function (assert) {
+ await render(hbs``);
+ assert.ok(find('#card'));
+ });
+
+ test('it renders with content', async function (assert) {
+ await render(hbs`
+
+ `);
+ assert.ok(find('button'));
+ });
+});
diff --git a/ui/core/app/styles/app.scss b/ui/core/app/styles/app.scss
index 8604fb63b..4b6c7f9a3 100644
--- a/ui/core/app/styles/app.scss
+++ b/ui/core/app/styles/app.scss
@@ -12,17 +12,7 @@
.project {
display: flex;
flex-direction: row;
- margin-bottom: 10px;
-
- a {
- text-decoration: none;
- text-transform: capitalize;
- color: #000;
- cursor: pointer;
- padding: 20px 30px 20px 20px;
- border: 1px solid #bac1cc;
- margin-right: 10px;
- }
+ padding: 10px 10px 10px 0;
}
}
}
diff --git a/ui/core/app/templates/orgs/org/projects/index.hbs b/ui/core/app/templates/orgs/org/projects/index.hbs
index c873611a3..312844b0d 100644
--- a/ui/core/app/templates/orgs/org/projects/index.hbs
+++ b/ui/core/app/templates/orgs/org/projects/index.hbs
@@ -12,9 +12,12 @@
{{#each @model as |project|}}
-
-
- {{project.name}}
-
+
{{/each}}