diff --git a/src/components/ListingCard/ListingCard.css b/src/components/ListingCard/ListingCard.css
new file mode 100644
index 00000000..bafa1e92
--- /dev/null
+++ b/src/components/ListingCard/ListingCard.css
@@ -0,0 +1,67 @@
+.listing {
+ display: flex;
+ flex-direction: column;
+ border-radius: 4px;
+}
+
+.squareWrapper {
+ display: block;
+ position: relative;
+ width: 100%;
+}
+
+/* Firefox doesn't support image aspect ratio inside flexbox */
+.aspectWrapper {
+ padding-bottom: 75%; /* 4:3 Aspect Ratio */
+}
+
+.thumbnail {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.info {
+ padding: 1rem;
+
+}
+
+.mainInfo {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.details {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+}
+
+.authorInfo {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+}
+
+.authorDetails {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+}
+.avatarWrapper {
+ display: block;
+ flex-basis: 44px;
+ width: 44px;
+ height: 44px;
+ margin-right: 1rem;
+}
+.avatar {
+ border-radius: 22px;
+}
diff --git a/src/components/ListingCard/ListingCard.js b/src/components/ListingCard/ListingCard.js
new file mode 100644
index 00000000..a868367d
--- /dev/null
+++ b/src/components/ListingCard/ListingCard.js
@@ -0,0 +1,71 @@
+import React, { PropTypes } from 'react';
+import { NamedLink } from '../../components';
+import css from './ListingCard.css';
+
+const ListingCard = props => {
+ const { id, title, price, description, location, review, author } = props;
+ const slug = encodeURIComponent(title.split(' ').join('-'));
+ return (
+
+
+
+

+
+
+
+
+
+ {title}
+
+
+ {price}
+
+
+
+ {description}
+
+
+
+ {location}
+
+
+ ({review.rating}/5){' '}
+ {review.count}
+
+
+
+
+
+

+
+
+
{author.name}
+
+ review: {author.review.rating}/5
+
+
+
+
+
+ );
+};
+
+ListingCard.defaultProps = { location: null, review: {}, author: {} };
+
+const { number, shape, string } = PropTypes;
+
+ListingCard.propTypes = {
+ id: number.isRequired,
+ title: string.isRequired,
+ price: string.isRequired,
+ description: string.isRequired,
+ location: string,
+ review: shape({ rating: string.isRequired, count: string.isRequired }),
+ author: shape({
+ name: string.isRequired,
+ avatar: string.isRequired,
+ review: shape({ rating: string.isRequired }),
+ }),
+};
+
+export default ListingCard;
diff --git a/src/components/ListingCard/ListingCard.test.js b/src/components/ListingCard/ListingCard.test.js
new file mode 100644
index 00000000..c016fffe
--- /dev/null
+++ b/src/components/ListingCard/ListingCard.test.js
@@ -0,0 +1,35 @@
+import React from 'react';
+import renderer from 'react-test-renderer';
+import { TestProvider } from '../../util/test-helpers';
+import { RoutesProvider } from '../../components';
+import routesConfiguration from '../../routesConfiguration';
+import ListingCard from './ListingCard';
+
+describe('ListingCard', () => {
+ it('matches snapshot', () => {
+ const listing = {
+ id: 123,
+ title: 'Banyan Studios',
+ price: '55\u20AC / day',
+ description: 'Organic Music Production in a Sustainable, Ethical and Professional Studio.',
+ location: 'New York, NY \u2022 40mi away',
+ review: { count: '8 reviews', rating: '4' },
+ author: {
+ name: 'The Stardust Collective',
+ avatar: 'http://placehold.it/44x44',
+ review: { rating: '4' },
+ },
+ };
+ const component = renderer.create(
+ (
+
+
+
+
+
+ ),
+ );
+ const tree = component.toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+});
diff --git a/src/components/ListingCard/__snapshots__/ListingCard.test.js.snap b/src/components/ListingCard/__snapshots__/ListingCard.test.js.snap
new file mode 100644
index 00000000..2c06adbf
--- /dev/null
+++ b/src/components/ListingCard/__snapshots__/ListingCard.test.js.snap
@@ -0,0 +1,86 @@
+exports[`ListingCard matches snapshot 1`] = `
+
+
+
+

+
+
+
+
+
+ Organic Music Production in a Sustainable, Ethical and Professional Studio.
+
+
+
+ New York, NY • 40mi away
+
+
+ (
+
+ 4
+
+
+ /5
+
+ )
+
+
+ 8 reviews
+
+
+
+
+
+
+

+
+
+
+ The Stardust Collective
+
+
+ review:
+
+ 4
+
+
+ /5
+
+
+
+
+
+
+`;
diff --git a/src/components/ListingCardSmall/ListingCardSmall.css b/src/components/ListingCardSmall/ListingCardSmall.css
new file mode 100644
index 00000000..4ac667ed
--- /dev/null
+++ b/src/components/ListingCardSmall/ListingCardSmall.css
@@ -0,0 +1,38 @@
+.listing {
+ width: 130px;
+ display: flex;
+ flex-direction: column;
+ border-radius: 4px;
+ margin-right: 1rem;
+}
+
+.squareWrapper {
+ display: block;
+ position: relative;
+ width: 100%;
+}
+
+/* Firefox doesn't support image aspect ratio inside flexbox */
+.aspectWrapper {
+ padding-bottom: 75%; /* 4:3 Aspect Ratio */
+}
+
+.thumbnail {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ width: 100%;
+ height: 100%;
+}
+
+.info {
+ width: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ padding: 0.5rem;
+ font-size: 0.8rem;
+ border: solid 1px #ccc;
+}
diff --git a/src/components/ListingCardSmall/ListingCardSmall.js b/src/components/ListingCardSmall/ListingCardSmall.js
new file mode 100644
index 00000000..c4578dd0
--- /dev/null
+++ b/src/components/ListingCardSmall/ListingCardSmall.js
@@ -0,0 +1,43 @@
+import React, { PropTypes } from 'react';
+import { NamedLink } from '../../components';
+import css from './ListingCardSmall.css';
+
+// X
+const ListingCardSmall = props => {
+ const { id, title, price, review } = props;
+ const slug = encodeURIComponent(title.split(' ').join('-'));
+ return (
+
+
+
+

+
+
+
+
+ {title}
+
+
+ ({review.rating}/5){' '}
+ {review.count}
+
+
+ {price}
+
+
+
+ );
+};
+
+ListingCardSmall.defaultProps = { review: {} };
+
+const { number, shape, string } = PropTypes;
+
+ListingCardSmall.propTypes = {
+ id: number.isRequired,
+ title: string.isRequired,
+ price: string.isRequired,
+ review: shape({ rating: string.isRequired, count: string.isRequired }),
+};
+
+export default ListingCardSmall;
diff --git a/src/components/ListingCardSmall/ListingCardSmall.test.js b/src/components/ListingCardSmall/ListingCardSmall.test.js
new file mode 100644
index 00000000..2f5e2eb1
--- /dev/null
+++ b/src/components/ListingCardSmall/ListingCardSmall.test.js
@@ -0,0 +1,35 @@
+import React from 'react';
+import renderer from 'react-test-renderer';
+import { TestProvider } from '../../util/test-helpers';
+import { RoutesProvider } from '../../components';
+import routesConfiguration from '../../routesConfiguration';
+import ListingCardSmall from './ListingCardSmall';
+
+describe('ListingCardSmall', () => {
+ it('matches snapshot', () => {
+ const listing = {
+ id: 123,
+ title: 'Banyan Studios',
+ price: '55\u20AC / day',
+ description: 'Organic Music Production in a Sustainable, Ethical and Professional Studio.',
+ location: 'New York, NY \u2022 40mi away',
+ review: { count: '8 reviews', rating: '4' },
+ author: {
+ name: 'The Stardust Collective',
+ avatar: 'http://placehold.it/44x44',
+ review: { rating: '4' },
+ },
+ };
+ const component = renderer.create(
+ (
+
+
+
+
+
+ ),
+ );
+ const tree = component.toJSON();
+ expect(tree).toMatchSnapshot();
+ });
+});
diff --git a/src/components/ListingCardSmall/__snapshots__/ListingCardSmall.test.js.snap b/src/components/ListingCardSmall/__snapshots__/ListingCardSmall.test.js.snap
new file mode 100644
index 00000000..e6a52348
--- /dev/null
+++ b/src/components/ListingCardSmall/__snapshots__/ListingCardSmall.test.js.snap
@@ -0,0 +1,44 @@
+exports[`ListingCardSmall matches snapshot 1`] = `
+
+
+
+

+
+
+
+
+`;