0
\$\begingroup\$

I have made a basic program using AngularJS that displays data from a JSON file, and filters it according to the user's selection (specifically FIFA 15 chemistry cards).

I did plenty of CSS work to it; mainly to test myself as I have barely used it. I have done some (minimal) work to make certain aspects respond to different size screens.

jsfiddle

HTML:

<link rel="stylesheet" href="style.css">
<!-- <link rel="stylesheet" href="style_small.css" media="screen and (min-width: 0px)">
        <link rel="stylesheet" href="style_med.css" media="screen and (min-width: 0px)">
        <link rel="stylesheet" href="style_medium.css" media="screen and (min-width: 1000px)"> -->
<link rel="stylesheet" href="animations.css">
<title>Chemistry Cards</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
<body ng-app="chemistry" ng-controller="MainCtrl">
    <div class="main_title">
        CHEMISTRY STYLES FINDER</div>
    <div class="subtitle">Select categories to filter cards</div>
    <div class="options_container">
        <label ng-repeat="option in options" class="option" id="option">
            <input type="checkbox" class="check" ng-model="option.selected" />  <span>{{option.name | uppercase}}</span>

        </label>
    </div>
    <div class="cards" id="cards">
        <div ng-repeat="card in cards | filter:itemFilter" class="card_background">
            <div class="card_title">{{card.name | uppercase}}</div>
            <div class="card_properties">
                <div ng-repeat="attr in card.attrs">{{attr}} <span class="card_properties_number">+{{num(card.attrs)}}</span>
                </div>
            </div>
        </div>
    </div>
    <script src="ang.js"></script>
</body>

AngularJS:

angular.module('chemistry', [])
    .controller('MainCtrl', function ($scope, $http) {

        //  was trying to get json from separate .json file, but just returns lots of '-'s:
    // $http.get('cards.json')
    //  .then(function(result){
    //  $scope.cards2 = result.data;
    // });

    //18 cards in total:
    $scope.cards = [{
        name: 'Sniper',
        attrs: ['shooting', 'dribbling']
    }, {
        name: 'Finisher',
        attrs: ['shooting', 'physical']
    }, {
        name: 'Deadeye',
        attrs: ['shooting', 'passing']
    }, {
        name: 'Marksman',
        attrs: ['shooting', 'dribbling', 'physical']
    }, {
        name: 'Hawk',
        attrs: ['pace', 'shooting', 'physical']
    }, {
        name: 'Artist',
        attrs: ['passing', 'dribbling']
    }, {
        name: 'Architect',
        attrs: ['passing', 'physical']
    }, {
        name: 'Powerhouse',
        attrs: ['passing', 'defending']
    }, {
        name: 'Maestro',
        attrs: ['shooting', 'passing', 'dribbling']
    }, {
        name: 'Engine',
        attrs: ['pace', 'passing', 'dribbling']
    }, {
        name: 'Sentinel',
        attrs: ['defending', 'physical']
    }, {
        name: 'Guardian',
        attrs: ['dribbling', 'defending']
    }, {
        name: 'Gladiator',
        attrs: ['shooting', 'defending']
    }, {
        name: 'Backbone',
        attrs: ['passing', 'defending', 'physical']
    }, {
        name: 'Anchor',
        attrs: ['pace', 'defending', 'physical']
    }, {
        name: 'Hunter',
        attrs: ['pace', 'shooting']
    }, {
        name: 'Catalyst',
        attrs: ['pace', 'passing']
    }, {
        name: 'Shadow',
        attrs: ['pace', 'defending']
    }];

    $scope.options = [{
        name: 'pace',
        selected: false
    }, {
        name: 'shooting',
        selected: false
    }, {
        name: 'passing',
        selected: false
    }, {
        name: 'dribbling',
        selected: false
    }, {
        name: 'defending',
        selected: false
    }, {
        name: 'physical',
        selected: false
    }];

    $scope.num = function (arr) {
        return arr.length > 2 ? 2 : 3;
    }

    $scope.count = true;

    $scope.itemFilter = function (item) {
        var filters = $scope.options.filter(function (element, idx, array) {
            return element.selected;
        }) || [];

        var matched = true;
        filters.forEach(function (option) {
            matched = matched && item.attrs.indexOf(option.name) >= 0;

        })
        return matched;
    };
});

CSS:

@import url(http://fonts.googleapis.com/css?family=Exo:200);
@import url(http://fonts.googleapis.com/css?family=Exo:300);
@import url(http://fonts.googleapis.com/css?family=Exo:500);
 body {
    /*background-image: url('http://i.imgur.com/gGwblunh.jpg');*/
    background-color: black;
    background-attachment: fixed;
    /*TEXT SETTINGS*/
    color: white;
    font-family:'Exo', sans-serif;
    font-weight: 200;
    min-width: 600px;
}
.main_title {
    margin: auto;
    width: 50%;
    padding: 10px;
    /*TEXT SETTINGS*/
    color: #ffcc00;
    font-family:'Exo', sans-serif;
    font-weight: 500;
    font-size: 300%;
    letter-spacing: -1px;
    text-align: center;
    text-shadow: 1px 1px 5px black;
}
.subtitle {
    color: #dcdcdc;
    text-align: center;
}
.options_container {
    margin: auto;
    padding: 5px;
}
.option {
    display: inline;
    /*TEXT SETTINGS*/
    color: #dcdcdc;
    /*light grey*/
    font-weight: 300;
    font-size: 200%;
    padding: 10px;
    /*Prevent div being highlighted upon click*/
    -webkit-user-select: none;
    text-shadow: 0px 0px 4px black;
}
.option:hover {
    -webkit-transition: 0.3s;
    color: white;
}
/*Colour the category text*/
 :checked + span {
    -webkit-transition: 0.1s;
    color: #ffcc00;
    text-shadow: 0px 0px 30px gray;
}
.check {
    /*Hide checkboxes but maintain functionality*/
    width: 0%;
    visibility: hidden;
}
.cards {
    display: inline-block;
    margin: 0px 50px;
}
.card_background:hover {
    -webkit-transition: 0.3s;
    background-color: #333333;
    /*TEXT SETTINGS*/
    color: #E1DDC2;
    text-shadow: 0px 0px 30px gray;
}
.card_background {
    cursor: default;
    border-radius: 5px;
    border: 2px solid black;
    width: 130px;
    height: 90px;
    background-color: #DBC282;
    margin: 20px;
    padding: 20px;
    display: inline-block;
    /*TEXT SETTINGS*/
    color: black;
    /*Prevent resizing of cards depending on number of attributes*/
    vertical-align: top;
}
.card_title {
    letter-spacing: -1px;
    font-size: 140%;
    text-align: center;
}
.card_properties {
    text-transform: capitalize;
}
.card_properties_number {
    /*  position: absolute;*/
    float: right;
}
/*------------------*/

/*TITLE RESPONSIVITY*/

/*------------------*/
 @media screen and (min-width: 0px) {
    .main_title {
        font-size: 150%;
    }
}
@media screen and (min-width: 580px) {
    .main_title {
        font-size: 200%;
    }
}
@media screen and (min-width: 800px) {
    .main_title {
        font-size: 300%;
    }
}
/*-------------------------*/

/*OPTIONS MENU RESPONSIVITY*/

/*-------------------------*/
 @media screen and (min-width: 1085px) {
    .options_container {
        font-size: 100%;
    }
}
@media screen and (min-width: 890px) and (max-width: 1085px) {
    .options_container {
        font-size: 80%;
    }
}
@media screen and (min-width: 700px) and (max-width: 890px) {
    .options_container {
        font-size: 60%;
    }
}
@media screen and (min-width: 0px) and (max-width: 700px) {
    .options_container {
        font-size: 45%;
    }
}

My questions for further improvement are:

  • What aspects of any of this code do not follow common best practises?
  • Do you notice any problems regarding layout in the CSS? If so, how could I improve them?
  • What aspects of any of this code would you do in a completely different way?
  • What can you suggest that might enhance this program?
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

Media Queries

There's no point in specifying a minimum width of 0, just leave off the media query all together.

There are very few instances where going mobile first will result in larger CSS than if you went desktop first:

@media (max-width: 699px) {
    .options_container {
        font-size: 45%;
    }
}

@media screen and (min-width: 700px) {
    .options_container {
        font-size: 60%;
    }
}

@media screen and (min-width: 890px) {
    .options_container {
        font-size: 80%;
    }
}

Questionable UX

You have "tabs" across the top to act as filters for what cards you display. When most users see tabs, they expect it to represent a single page containing all of the items that belong under that heading. By hiding the fact that it is a checkbox rather than a radio, you're confusing the user.

Prefixes

Don't use prefixes unless you're also going to use the unprefixed version of the property.

:checked + span {
    -webkit-transition: 0.1s;
    color: #ffcc00;
    text-shadow: 0px 0px 30px gray;
}

Inefficiencies

Rather than adjusting the visibility + forcing the element's width to nothing, just use the display property:

.check {
    display: none;
}

Adjust the font-size on only the elements you want adjusted to the exact value you want it to be. Don't set the font-size of everything to 45% and then increase the size child elements later on (.options_container & .options).

Duplicate ids

Ids must be unique, but you're repeating cards when you generate your content.

\$\endgroup\$
2
\$\begingroup\$
  1. Load jQuery before Angular, so that it replaces jq.
  2. ng-repeat is often better by adding a track by statement.
  3. It is commented out, but don't use $http in controllers. Move that to a service. The advice might apply then to your raw Json data...
  4. All your options have the same id. Just don't put an id here.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.