Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found
Not Found

All comments

  • Samuelโ€ข 840

    @Samuel-Amaro

    Submitted

    This project was very challenging from the start, it took longer than I had estimated, the biggest challenges were building accessible components with focus management, in addition to the data structure for the client-only boards, there were many challenges, which I encountered when trying to build this project, however it was gratifying to see the result, feel free to evaluate the code and give me tips on what I can improve.

    P

    @christopher-adolphe

    Posted

    Hi @Samuel-Amaro ๐Ÿ‘‹

    You did a great job on this challenge. ๐Ÿ‘ I had a quick look at the application as well as the code base and here's my feedback:

    • The buttons for the dropdown menus are too small (4px by 20px) and as a result, this reduces the both touch and click accuracy. For better user experience, the recommended size of icon buttons should be in a range of 42 to 72px.
    • I think the Content component file contains too many sub components. You could have moved ColumnBoard, TaskList and CardTask in dedicated component files under the components directory. These components indeed compose your content but each of them have different responsibilities which is why I think good to separate them. However, this one is only a personal preference ๐Ÿ˜‰ I know React gives you the freedom of organising your folder structure any way you want.

    I hope this helps.

    Keep it up.

    Marked as helpful

    0
  • P

    @christopher-adolphe

    Posted

    Hi @fontainm ๐Ÿ‘‹

    You did a great job on this challenge. ๐Ÿ‘

    I had a look at your code and here are a few things that might help you with your class names and the structure of your markup:

    • I get the impression you tried to use the BEM methodology to write your CSS but you did not applied it 100%. You have defined .notifications as a Block which has the .notifications__header and .notifications__list as its Elements. This is good ๐Ÿ‘. However, the .notifications Block has other Elements inside it which you chose to target differently. To be honest, it's not a bad thing as such but if you'd like to go the full BEM way, here's a suggestion for the class names and markup:
    <div class="notifications">
      <header class="notifications__header">
        <h1 class="notifications__title">Notifications</h1>
        <span class="notifications__count">{{ countUnread }}</span>
        <button type="button" class="notifications__btn">Mark all as read</button>
      </header>
    
      <ul class="notifications__list">
        <li class="notifications__item">
          <img class="notifications__avatar"/>
    
          <div class="notifications__content">
            <div class="notifications__sender"></div>
    
            <span class="notifications__time"></span>
    
            <div class="notifications__body"></div>
          </div>
        </li>
      </ul>
    </div>
    

    With the above markup, you will end up with the following CSS:

    .notifications { ... }
    
    .notifications__header { ... }
    
    .notifications__title { ... }
    
    .notifications__count { ... }
    
    .notifications__btn { ... }
    
    .notifications__list { ... }
    
    .notifications__item { ... }
    
    .notifications__item--unread { ... } // This will be your `Modifier` to style an unread item
    
    .notifications__avatar { ... }
    
    .notifications__content { ... }
    
    .notifications__sender { ... }
    
    .notifications__time { ... }
    
    .notifications__body { ... }
    

    As you can see, if you follow the BEM methodology, your final CSS is completely flat, i.e you'll not have selectors like .notifications__list .notification .message { ... }

    • One last thing, use semantic elements where needed. For example, the Mark all as read should be a <button> instead of a <div>

    I hope this helps.

    Keep it up.

    Marked as helpful

    1
  • Abed Fetratโ€ข 450

    @abedfetrat

    Submitted

    This is another of my projects built with React. I used Styled components for the styling. For the drag to reorder function I was thinking of implementing it without any libraries, but after doing some research I found out that the native drag and drop API didn't work with touch screen devices. So I ended up using Framer Motion because of it and that we get nice animations for free.

    Let me know If you have coded the drag and drop functionality without using libraries, as I'm still interested to see how it's done. โœŒ

    P

    @christopher-adolphe

    Posted

    Hi @abedfetrat ๐Ÿ‘‹

    You did a great job on this challenge. ๐Ÿ‘

    One small thing I have noticed, given I have added 3 new tasks, when I drag the last task from the bottom of the list and drop it at the top, it gets marked as completed. I'm pretty sure this has something to do with the region of the task that's clickable to toggle as active/completed. If you initiate the dragging from this region, when you'll drop the item, the toggling is also triggered. But strangely, I notice it is only happening when dragging an item from the bottom to the top.

    Framer Motion looks really cool. I want to try it on my next project. I've always used CSS modules with React but when I see how clean you've been able to keep those styles within your components, I think I should styled components a try as well.

    Keep it up.

    Marked as helpful

    0
  • P

    @christopher-adolphe

    Posted

    Hi @MuriWolf ๐Ÿ‘‹

    You did a great job on this challenge. ๐Ÿ‘ The markup, styling and component composition are well done. ๐Ÿ‘Œ

    One easy fix to get rid of these accessibility report errors/warnings is to set a value to the lang attribute of the <html> tag like so <html lang="en"> in your public directory.

    I have been trying to do various actions in the application and I have noticed that when I reply to a comment, my reply is not added right away. I had to add another reply then both of them appeared. I'm not sure if it's because your REST API is hosted on Heroku and it is responding slowly but you might want to double check that as it hinders the user experience.

    In case it's your REST API which is responding slowly, there is a way you could give a faster feedback to the users. Right now, you've written the CRUD operations of your component in a pessimistic update style, meaning; you are making the calls to the REST API and then you are updating the UI. You may give the user the impression that application is faster by doing the opposite, i.e; you will update the UI first and then make the calls to REST API. This is an optimistic update. ๐Ÿ˜‰ However, when doing so, you will need to keep a reference of the previous state so that if ever an error occurs while doing the update, you can safely revert to that previous state. Below is an example of what an optimistic update looks like in code:

    // 1)Keeping a reference of previous state
    const previousComments = [ ...this.comments ];
    
    // 2) Updating the UI
    this.comments = [ ...this.comments, this.newComment ];
    
    try {
      // 3) Calling the endpoint to post the new comment
      const response = await fetch('some-endpoint-url', this.newComment);
    } catch (error) {
      // 4) Reverting to the previous state if an error occurs
      this.comments = [ ...previousComments ];
      // 5) Giving a feedback that an error occurred
      alert(`Sorry, an error occurred while adding new comment: ${errror}`);
    }
    

    The above code snippet is not Vue.js specific but I think you'll be able to implement it somehow.

    I hope this helps.

    Keep it up.

    Marked as helpful

    1
  • P

    @christopher-adolphe

    Posted

    Hi @Shady-Omar ๐Ÿ‘‹

    You did a great job on this challenge. ๐Ÿ‘ Here are a few things that I have noticed and that you might want to check in order to improve your solution.

    HTML:

    While the page you built is good, you could improve it by using HTML semantic elements in the following ways:

    • For the navigation, you could wrap the <ul> element in a <nav> element like this:
    <nav>
      <ul>
        <li>
            <a href="#">Home</a>
        </li>
        <li>
            <a href="#">New</a>
        </li>
        <li>
            <a href="#">Popular</a>
        </li>
        <li>
            <a href="#">Trending</a>
        </li>
        <li>
            <a href="#">Categories</a>
        </li>
      </ul>
    <nav>
    
    • The <a> element is also missing for your navigation.
    • Try as much as possible to refrain from duplicating HTML elements on your page because this hinders the maintainability of your code. You can use the same markup for the navigation on both mobile and desktop and adapt the styles by using CSS media queries.
    • You could replace the <div class="top-content"> and <div class="bottom-content"> by a <section> element.
    • For the "New" section, you could replace the <div class="top-right"> as well as it children <div class="text"> elements by an <article> like this:
    <article class="new-post">
      <h2>New</h2>
    
      <article class="new-post__item">
        <h3>Hydrogen VS Electric Cars</h3>
        <p>Will hydrogen-fueled cars ever catch up to EVs?</p>
      </article>
    
      ...
    </article>
    
    • You could replace the <div class="topic"> by an <article>.
    • The "Read More" looks like a <button> element but it is in fact an <a> element that will allow the user to navigate to a page where he/she will have access to the whole post. Therefore you should probably define a CSS rule that you can apply to an <a> element so that it looks like a button. ๐Ÿ˜‰
    • Instead of using CSS classes to handle responsive images, you could use the <picture> element together with the <source> element to achieve same like below:
    <picture>
      <source media="(min-width: 1110px)" srcSet="assets/images/image-web-3-desktop.jpg" />
      <source media="(min-width: 768px)" srcSet="assets/images/image-web-3-mobile.jpg" />
      <img src="assets/images/image-web-3-desktop.jpg" alt=alt="web3" />
    </picture>
    

    CSS:

    • In general try to give meaning names to your CSS classes. This will not only help you as well as those you collaborate with you on a project to understand much faster what a particular CSS rule should be used for but will also help to organise your styles better.
    • Try to keep the level of nesting to a maximum of 3 levels deep as this hinders the reusability of your CSS rules. In other words, when you are overly specific, you will have to increase the nesting even more if you want to override any particular CSS rule. The rule of thumb is to define your generic rules first and then define more specific ones for other use cases.
    // DON'T โ˜๏ธ๐Ÿ‘Ž
    main .content .top-content .top-right .text h3 {
      margin-bottom: 1rem;
      font-weight: 800;
    }
    
    // DO ๐Ÿ‘Œ๐Ÿ‘
    h3 {
      margin-bottom: 1rem;
      color: hsl(240, 100%, 5%);
      font-size: 1.25rem;
      font-weight: 800;
    }
    
    .article__item h3 {
      margin-bottom: 0.5rem;
      color: hsl(36, 100%, 99%);
      font-size: 1rem;
    }
    

    RESPONSIVE:

    • On the tablet viewport, the content looks squeezed and there is a huge white space under the "The Bright Future of Web 3.0" section. It would look better if you set this top section in a single column layout on this viewport.

    I hope this helps.

    Keep it up.

    Marked as helpful

    0
  • P
    Daveโ€ข 5,245

    @dwhenson

    Submitted

    This is my second project using React and first using styled components. I put some details in the readme, but some issues/questions I have are:

    • One issue I had with styled components is keeping track of whether the components in the file were now React components or styled components! I also struggled mapping the styled components to the rendered DOM in the dev tools, which made debugging a bit tricky in some cases. How to people manage this?
    • 'React.useEffect` is asking for additional dependencies in a couple of places in board.js (see FIXMEs). The only way I could get the app deployed was to ignore them. If I add them as suggested I end up with infinite loops. I wasn't sure how to deal with this if any one knows how to deal with this I would love to know.
    • Board.js is a bit of a monster, I had a look at refactoring things, but I couldn't see any obvious ways to do this. I considered moving the remaining game logic functions out, but they all change state so this didn't seem sensible. Any suggestions would be welcome!

    Thanks in advance for any suggestions and feedback. Cheers Dave

    P

    @christopher-adolphe

    Posted

    Hi @Dave ๐Ÿ‘‹,

    You did a great job on this challenge. ๐Ÿ‘ I had a great time playing the game too. ๐Ÿ˜‰

    I saw you question about the issues you are having with dependency array of React's useEffect hook. I had a quick look at your code for the Board.js component and here are a few things that I have noticed which might be why you are facing those issues. I'll give you some rules/guidelines that I use myself when I work with the useEffect hook and I'll try to keep it simple so that you'll know where to go from here.

    Rule 1: Don't ignore the warnings about the dependency array

    • We get warnings when React sees we are doing something wrong. You should take a step back and carefully review your implementation of the useEffect hook.

    Rule 2: Know the data types of the dependencies being passed to the array

    • This is where most trouble come from. We should carefully check the data types of the different elements being passed to the dependency array. If the dependencies are primitive data types (i.e: string, number, boolean, null, undefined) we don't have much to worry as they are compared by value. But if they are reference data types (i.e array, object, function, date), the useEffect hook will compare them by reference (i.e their pointer in memory) and each time the component renders, we create a new reference to those dependencies and as the useEffect hook detects they are different, it reruns hence resulting in an infinite loop. ๐Ÿ˜ฑ Hopefully, React provides us with tools to work around this problem; they are the useMemo and useCallback hooks. Consider the useEffect below which was extracted from your Board.js component:
    React.useEffect(() => {
        if (gameType !== "CPU") return;
        if (marker !== turn) {
          renderSquareChoice(computerMove(squares, marker));
        }
      }, [gameType, turn]);
    

    renderSquareChoice is a function inside your component and you are using it inside useEffect, so React prompts you that it needs to be added to the dependency array. Now, you add it as a dependency, you get an infinite loop because renderSquareChoice is a function meaning it is a reference data type. To fix this, you need to wrap the renderSquareChoice with useCallback like so:

    const renderSquareChoice = useCallback((square) => {
        if (status || squares[square]) return;
        const nextSquares = [...squares];
        nextSquares[square] = turn;
        setSquares(nextSquares);
      }, []);
    

    And you will be able to add it to useEffect as a dependency without causing an infinite loop. NOTE: In the above code, useCallBack also has a dependency array, you might need to add status and squares as dependencies.

    • There are cases where we don't need to pass the entire object or array as a dependency to useEffect. For example, if my component has a person state/prop but my effect is only dependent on the age property of that person state. All we need to do is to pass this age property to useEffect like below:
    const [ person, setPerson ] = useState({
      name: 'Chris',
      age: 36,
      role: 'guest'
    });
    
    useEffect(() => {
      // some side effect that needs to run whenever the age of `person` state is mutated
    }, [person.age]);
    

    So we are not passing the entire person state which an object and therefore a reference data type because each render of the component would create a new reference for the person state. Instead, we pass only the age property which is a number and therefore a primitive data type. I illustrated a simple case here but for complex cases the useMemo hook would be more appropriate. This feedback is already very long, I suggest you to read more about useMemo.

    Rule 3: Use the functional variant of the state setter function inside useEffect

    • Whenever useEffect is dependent on a state which is mutated inside it, you should use the functional variant of the state setter function. Taking another example extracted from the Board.js component:
    // Update total scores
      React.useEffect(() => {
        if (status === null) return;
        const newScore = { ...score };
        newScore[status] += 1;
        setScore(newScore);
      }, [status]);
    

    This useEffect should be refactored as follows:

    // Update total scores
    React.useEffect(() => {
      if (status === null) return;
      
      setScore((prevScore) => {
        const newScore = { ...prevScore };
    
        newScore[status] += 1;
    
        return newScore;
      });
    }, [status]);
    

    By doing so, our useEffect is no more dependent on score state and hence it isn't needed in the dependency array.

    I know that's a lot to read as feedback but I tried to keep it as lean as possible. useEffect is a big topic in itself. I also got these issues while tackling the Coffeeroasters challenge with React (still in progress ๐Ÿ˜‰). The more you practice, the better you'll get at it.

    I hope this helps you as a starting point.

    Here are some resources from Jack Herrington that helped me:

    Keep it up.

    Marked as helpful

    1
  • P

    @christopher-adolphe

    Posted

    Hi @Briuwu ๐Ÿ‘‹

    You did a great job on this challenge. ๐Ÿ‘ Here are a few things that I have noticed and that you might want to check in order to improve your solution.

    • View plans and How we work are links that are styled as buttons. So your markup should be <a href="#">VIEW PLANS</a> instead of <button>VIEW PLANS</button>
    • In the navigation bar and the footer, How we work, Blog, etc these should be anchor tag as well.
    • There is a small issue in intro section on tablet. The image seems too large for this viewport width.
    • In the Footer.jsx component, you could also leverage on the map() method to generate the links by adding them to an array like you did in the Info.jsx component.

    src/data/footerLinks.js

    export const footerLinks = [
      {
        menuName: 'Our Company',
        items: [
          {
            title: 'How we work',
            link: '/how-we-work',
          },
          {
            title: 'Why insure ?',
            link: '/why-insure',
          },
          {
            title: 'View plans',
            link: '/view-plans',
          },
        ]
      },
      {
        menuName: 'Help me',
        items: [
          {
            title: 'FAQ',
            link: '/faq',
          },
          {
            title: 'Terms of use',
            link: '/terms-of-use',
          },
          {
            title: 'Policies',
            link: '/policies',
          },
        ]
      }
    ];
    

    src/components/Footer.jsx

    import { footerLinks } from '../data/footerLinks';
    import { Link } from 'react-router-dom';
    
    const Footer = () => {
      return (
        //...
        <div className="footer-container">
          {
            footerLinks.map((menu, index) => (
              <div className="footer-all" key={`menu-${index}`}>
                <h4 className='footer-title'>{ menu.title }</h4>
                {
                  menu.items.map((item, index) => (
                    <Link to="{item.link}" className="footer-info" key={`link-${index}`}>{item.title}</Link>
                  )
                }
              </div>
            ))
          }
        </div>
        //...
      )
    }
    

    I hope this helps.

    Keep it up.

    Marked as helpful

    1
  • Jason Moodyโ€ข 300

    @MoodyJW

    Submitted

    Hello, thanks for checking out my project!

    The worst part of it was updating Angular and killing all of the package vulnerabilities. I initially wanted to use Material as I'm familiar with it, but quickly realized it would be easier to just work from scratch than to modify Material components. I used a handful of RxJS operators as well.

    I'm happy to hear any suggestions, criticisms, advice, etc. I think my styling is pretty weak with regards to using the optimal approach; however, I think everything at least looks like it should on mobile/tablet/desktop.

    P

    @christopher-adolphe

    Posted

    Hi @MoodyJW ๐Ÿ‘‹

    You did a great job on this challenge. ๐Ÿ‘ The component composition is well done. ๐Ÿ‘Œ

    The only thing I have noticed is that the <body> tag is inside your root AppComponent and in the rendered index.html page you now have a <body> tag nested in another <body> tag which is unfortunately not valid. I understand you did this to implement the theme switching feature. Fortunately, you could resolve this with the help of Renderer2 from @angular/core module. By injecting this service and the DOCUMENT into your AppComponent class, you will be able to add/remove the theme class on the <body> element the "Angular way" without having it inside the root AppComponent template file. Your AppComponent should look something like this after doing this change:

    import { Component, OnInit, Inject, Renderer2 } from '@angular/core';
    import {DOCUMENT} from "@angular/common";
    
    /**
    * Your other imports, component decorator etc
    */
    export class AppComponent implements OnInit {
      theme = window.matchMedia('(prefers-color-scheme: light)').matches
        ? 'light-theme'
        : 'dark-theme';
      user!: User;
    
      constructor(
        @Inject(DOCUMENT) private document: Document,
        private renderer: Renderer2,
        private usersService: UsersService
      ) {}
    
      ngOnInit() {
        this.renderer.addClass(this.document.body, this.theme);
        this.usersService
          .getUser('octocat')
          .pipe(first())
          .subscribe((user) => {
              this.user = user;
        });
      }
    
      toggleTheme() {
        const bodyElem = this.document.body;
        const isLightTheme = bodyElem.classList.contains('light-theme');
    
        if (!isLightTheme) {
          this.renderer.removeClass(bodyElem, 'dark-theme');
          this.renderer.addClass(bodyElem, 'light-theme');
        } else {
          this.renderer.removeClass(bodyElem, 'light-theme');
          this.renderer.addClass(bodyElem, 'dark-theme');
        }
      }
    
      userReturned(user: any) {
        this.user = user;
      }
    }
    

    However, with this change, the currentTheme argument passed to the toggleTheme() method would no more be required and therefore, the ThemeToggleComponent would no more need to emit a custom event. Even better, you could move the toggleTheme() method from the root AppComponent to the ThemeToggleComponent since it is the one responsible of this feature. The root AppComponent would thus be only responsible of initialising the user property.

    I hope this helps.

    Keep it up.

    Marked as helpful

    1
  • Alucardโ€ข 870

    @Alucard2169

    Submitted

    I had fun building this project, i wanted to do this for a really long time, cause it looked cool ๐Ÿ˜‹.

    I hope you like it.

    feedback will be appreciated.

    P

    @christopher-adolphe

    Posted

    Hi @Alucard2169 ๐Ÿ‘‹

    You did a great job on the overall for this challenge. ๐Ÿ‘ I really like the subtle hover effect on the navigation links ๐Ÿ‘Œ. Here are a few things that I have noticed and that you might want to check in order to improve your solution.

    • It might be a good thing to have a skip to content link that redirects the user directly to main content is ever they are using assistive technology to visit this landing page.
    • In the vr section, the left and right paddings are not necessary for desktop as they offset the content while in the design, the content of this section is aligned with the next one.
    • In the creation section, I'm not sure if is a good option to use the images a background image as they seem to be part of the content rather than just decorative.
    • On tablet, the <header> is quite huge, maybe you should consider reducing the height and as for the main content, I think that there is ample space to display the creation section in a 2 column layout.

    I hope this helps.

    Keep it up.

    1
  • P

    @christopher-adolphe

    Posted

    Hi @Reallyvane ๐Ÿ‘‹

    You did a great job in completing this challenge. ๐Ÿ‘ The component is faithful to the design. ๐Ÿ‘Œ Below are a few things that I have noticed and you might want to check in order to improve your solution.

    • When I look at this component, I would expect to see a <form> element with <input type="radio"/> elements for the different ratings and a submit <button> in the HTML markup. However, it is not the case in your solution. While the component seem to be working fine for the general users, those with assistive technology might struggle to interact with it. This is why I would suggest you to use HTML elements that are built to take user inputs when it is the case. For example <input type="radio"/> or <input type="checkbox"/> to choose from a list of options instead of <div> elements. This will also simplify things on the JavaScript side. ๐Ÿ˜‰
    • I also noticed that you have set a default rating value of 3 but at the moment, there is no visual cue that communicates that to the user. You should add the active class by default on the corresponding <div class="rating-btn"> element. Again, if it was a <input type="radio"/>, you would have used the checked attribute to achieve that. On the other hand, if you don't wish to set a default rating value, you would then have to add a check in the onSubmit() function like this:
    let stars_score;
    
    function onSubmit() {
      // Returning early if stars_score is not set
      if (!stars_score) {
        return;
      }
      
      card_content_1.classList.add("hide");
      score.textContent = stars_score;
      card_content_2.classList.remove("hide");
    }
    

    I hope this helps.

    Keep it up.

    Marked as helpful

    1
  • Tiffaniโ€ข 30

    @tiffanicodes

    Submitted

    I really struggled with figuring out the best grid units and the best number of columns to properly line up the gallery photos. I would love some input on this!

    P

    @christopher-adolphe

    Posted

    Hi @tiffanicodes, ๐Ÿ‘‹

    You did a nice job in completing this challenge. ๐Ÿ‘ I have recently completed the same project and I think I can give you some tips on how you could improve your solution or at least think of a different approach.

    • I see that you have built the main content section using the grid-area property from CSS grid. While this is not a bad approach, it is not a flexible solution. Here are 2 resources that will surely help you come up with a more efficient layout. Travis Horn - Responsive grid, CSS-TRICKS - Responsive Grid
    • In order to render the hero title in black and white, you can apply the mix-blend-mode property. It can have different values depending on the result you want to get, in the case of this challenge, the exclusion value does the job. ๐Ÿ˜‰ Read more about it here.
    • The spacings in the footer section need to be reviewed as at the moment it looks wider than the header and main sections. I would suggest that you add a wrapping <div class="container"> element inside the different sections of your page to which you then apply a max-width. This will resolve the problem as well as on the location page.
    • On the CSS side, I would suggest that you refrain from using id as selectors to style elements as this hinders reusability.

    I hope this helps.

    Keep it up.

    Marked as helpful

    1
  • Anna Leighโ€ข 5,135

    @brasspetals

    Submitted

    Hi, everyone! ๐Ÿ‘‹ It feels great to finally submit this solution. Iโ€™ve wanted to do this challenge since I started doing Frontend Mentor projects. It was definitely a learning experience (see: README), and hopefully Iโ€™ve made up for the lack of effects in my Sunnyside solution. ๐Ÿ˜†

    The parallax effect is only on desktop and tablet. Itโ€™s also turned off, along with all animations and page transitions, for those who prefer reduced motion. ๐Ÿ‘

    Questions:

    • This was my first time implementing a โ€œtabbedโ€ interface. I opted to use button elements for the tabs. Is there a better and/or more accessible way to create tab functionality?
    • Iโ€™m concerned that the image animations on the tabbed Events section are getting messed up if the image doesnโ€™t load before the animation triggers. Is there a way to preload images from picture elements in a way thatโ€™s responsive and also works with multiple srcsets? Update: I refactored this section a bit, making some tweaks to the animations and putting all content in the DOM rather than dynamically loaded. The flashing issue is now fixed. Huge thanks to Dave and Christopher for their help on this! ๐Ÿ™
    • How is the form accessibility on the booking page? I donโ€™t believe the second page was included in the report.
    • Sadly, I didn't manage to style the AM/PM selector like the design because I used select and option elements. Do you know of a better method that would be both accessible and match the dropdown in the design?
    • Not sure how I feel about the menu list being parallax on desktop. Does it look too wonky or have I just been staring at it for too long? ๐Ÿ˜‚

    Thanks for taking the time to look at my solution! Feedback is always greatly appreciated. ๐Ÿ˜„

    P

    @christopher-adolphe

    Posted

    Hi @brasspetals,

    You did a great job on this challenge. The parallax effect is perfect. It really accentuates this feeling of depth on the page. ๐Ÿ‘Œ๐Ÿ‘Œ

    As for the tabbed content, it is still flashing at the moment. I tried to toggle the fadein and fadeout classes manually on the <img /> element and I noticed that the flashing occurs when the fadeout class is applied. The image is still visible with this class applied to it. Maybe that's where you need to have a closer look. ๐Ÿค” I recently watched a video from Kevin Powell about animating from display none. Maybe that could help also you.

    I also noticed that on large viewports (1600px and above) there are like to 2 black stripes on the each side of the page and it is caused by the fact that the <div class="container"> element is restricted by a max-width. Was this intentional ? ๐Ÿค”

    Keep it up.

    Marked as helpful

    1
  • P
    Briuwuโ€ข 750

    @Briuwu

    Submitted

    Hello~! (โ‰งโˆ€โ‰ฆ)ใ‚ž

    For this challenge, I have finally decided to try framer-motion, but only the basic stuffs (lol), I still struggled on managing my CSS, so it's really messy... maybe I should consider learning styled components soon.

    HTML ISSUES: I can't seems to fix it, it says I have a duplicate id on my linear gradient ?? I don't really understand it, so please if you have any solutions please comment it. (fixed)

    Please do check this one out! If you have any feedback or tips! Please comment it!

    Thank you! (โ‰งโˆ‡โ‰ฆ)๏พ‰

    P

    @christopher-adolphe

    Posted

    Hi @Briuwu ๐Ÿ‘‹

    You did a great job in completing this challenge using React. ๐Ÿ‘ Below are a few things that I have noticed and you might want check in order to improve your solution.

    • On large viewports the <Social /> and <Overview /> components align to the left side. To correct this, simply apply margin-left: auto; and margin-right: auto; to them or you could apply display: flex; on the <main> element.
    • On tablet maybe it would be better to keep a 2 column layout instead of 3 for the <Social /> and <Overview /> components. At the moment, you have one card overflowing to the next line which creates an unnecessary whitespace.
    • As for the CSS, it is not as messy as you might be thinking. ๐Ÿ˜‰ You could certainly improve on the organization of your partials. One thing I have noticed though, it would be better to move the compiled CSS in a different directory which you could call css and keep your SASS files in another directory called sass. This way when you deploy the finished project, only the compiled CSS is bundled in the deployment. However, if this is already being taken care of during the build process, please ignore this comment. ๐Ÿ˜‰
    • You could also consider using CSS modules stylesheet, which aim at scoping the style to the component. For example your <Dashboard /> component would have its own Dashboard.module.scss file. I think this also greatly helps in keeping your CSS organized. You can read more here

    I hope this helps.

    Keep it up.

    Marked as helpful

    1
  • P

    @christopher-adolphe

    Posted

    Hi @toriola998, ๐Ÿ‘‹

    You did a great job in completing this challenge using Vue. ๐Ÿ‘ Below are a few thing that I have noticed and you might want to check in order to improve your solution.

    • On larger viewports the overall content on the pages stretches horizontally thus making the images appear huge. I see that you have used padding for the spacing on each sides. I would suggest you use a wrapping <div> element inside each <section> to which you could apply a max-width.
    • For the cart button, it would be better to use a <span> for the cart count because a <p> element cannot be the child of a <button> element.
    • When I tried to add the same product twice to the cart, it was added as a new item in the cart instead of showing x2. The increment and decrement feature is also missing on the product details page.
    • The overall composition of your page structure could be improved by moving the <NavBar />, <header />, <main> and <TheFooter /> components to the App.vue root component. This would reduce repetition in the different views component.
    • The social icons in the footer should have links to redirect the user to the respective social media website.

    I hope this helps.

    Keep it up.

    0
  • P

    @christopher-adolphe

    Submitted

    Hello frontend friends! ๐Ÿ‘‹

    This was long overdue but I finally completed my 2nd challenge on Frontend Mentor. ๐ŸŽ‰ This project was an opportunity for me to test new approaches on the way I write my CSS.

    Major challenge(s):

    • I started using Sass maps with @each rules to generate helper classes easily. It is not perfect but I think is a good start.
    • I started using custom CSS properties to better organise my CSS and reduce repetition.
    • Getting the grid as per the design was more challenging than I thought. I initially started with by defining the height of each row for the main content for different viewport widths but I soon realised that this was difficult to maintain. I finally found an efficient solution by using the minmax() function with the grid-template-columns and grid-template-rows properties.

    Your feedback would be much appreciated, especially if you have other ideas about how to better implement custom CSS properties in your workflow.

    Thanks in advance. ๐Ÿ™

    Art gallery website | HTML, CSS, JS

    #sass/scss#typescript#webpack#gsap

    5

    P

    @christopher-adolphe

    Posted

    Hi @RMK-creative,

    Thank you. ๐Ÿ˜‰

    Yeah feel free to go through the git repo, you'll hopefully find something useful. As for GSAP, I recently started using it as well and I can tell you that you can do awesome stuff with that.

    As for the interactive map, this morning I did some changes to move the mapbox access token to an environnement variable on Netlify. I must have done something wrong. Thanks for notifying. I'll check it out.

    Best regards.

    0
  • Shashree Samuelโ€ข 9,260

    @shashreesamuel

    Submitted

    I had no major problem completing this challenge however I definitely would like some feedback regarding the space that shows up below the footer section since I have applied a min-height on the body element yet the space is still visible.

    Also let me know if I could have done any aspect of this design in a more reliable or effective way and generally any great practices that I should be taking into consideration and implementing.

    Thanks for the feedback in advance

    Cheers Happy coding

    P

    @christopher-adolphe

    Posted

    Hi @TheCoderGuru ๐Ÿ‘‹,

    You did a great job in completing this challenge. ๐Ÿ‘ Everything is nice and clean. ๐Ÿ‘Œ I have noticed a few thing that you might want to check.

    • On large viewports, the overall content of the page is stretching horizontally thus creating a huge gap between the 2 column content. Adding a wrapping <div> element with a max-width inside each section should correct that. ๐Ÿ˜‰
    • When the viewport is around 1024px and up to 1200px, the content of the 1st column of the hero section is squeezed. gap: 15rem; is too much on this viewport range.
    • The shadow around the feature cards in the main content is missing. Moreover, the text content and the image should alternate positions. You could achieve this by applying the following styles:
    .grid-container:nth-child(even) {
      .grid__content {
        order: 2;
    
       + div {
         order: 1;
       }
      }
    }
    

    However, for this to work properly, the absolute positioning should be removed on the <img />. By looking at the design, I don't think position: absolute; is even required here.

    • I would have suggested you to write the markup for these feature cards in mobile-first approach; meaning the image content would be the 1st child element of the card and then the text content, something like this:
    <div class="grid-container">
      <div class="grid__image">
        ...
      </div>
    
      <div class="grid__content">
        ...
      </div>
    </div>
    

    Since this is the default order of the content, you wouldn't have much style to add on mobile and as the viewport widens, you could then apply the suggested above style to create the alternating content position like this:

    @media (min-width: 1024px) {
      .grid-container:nth-child(even) {
        .grid__image {
          order: 2;
        }
        
        .grid__content {
          order: 1;
        }
      }
    }
    
    • In the footer, I think About Us, What We Do etc. as well as the social media icons should be links.

    I hope this helps.

    Keep it up.

    0
  • Francisldnโ€ข 250

    @francisldn

    Submitted

    The focus of this project is about creating layout using CSS, in particular CSS grid. I was unable to render the heading - Modern Art Gallery in 2 different colours. Any feedback/help in this would be much appreciated.

    Art gallery website challenge

    #react#react-router

    3

    P

    @christopher-adolphe

    Posted

    Hi @francisldn ๐Ÿ‘‹

    You did a good job on completing this challenge with CSS grid and React. ๐Ÿ‘ I am currently working on the same challenge and I'm only left with a few minor refinements before posting my solution. I wanted to review your solution so as to compare how we approached this challenge and if possible share some tips with you. ๐Ÿ˜‰ Below are few things I have noticed and you might want to check in order to improve your solution.

    • Rendering the main heading in 2 colours is surely the part that makes you scratch your head when you see the design file for the 1st time. ๐Ÿ˜† But I can tell you that you can achieve that by using a single CSS property called mix-blend-mode. It can have different values depending on the result you want to get, in the case of this challenge, the exclusion value does the job. Read more about it here. You can apply it to your .heading class like this:
    .heading {
      mix-blend-mode: exclusion;
    }
    
    • Along the same vein, I think that it would be better to use an <h1> element for the Modern Art Gallery text instead of a <div> and then in the main content section use <h2> elements for the other heading.
    • The arrow for the Our location link is larger as compared to the design. Moreover, it is a link so this means that it should be an <a href="/location"></a> element instead of a <div>. Since you are using React and React Router, it makes sense to use the <Link to="/location"> component for this. To get the arrow to the correct size, you could refactor this part like this:
    <Link className="btn" to="/location">
      <span className="btn__text">Our Location</span>
      <span className="btn__icon">
        <img className="arrow" ref={locationImageRef} src={arrowRight} alt="" />
      </span>
    </Link>
    

    You can then use the .btn__icon class to style the part of the link with the golden background and the arrow. Also note that you can leave the alt attribute of the <img/> element blank since this is a decorative image.

    • For better accessibility, please use a <main> element to wrap the main content of the page and a <footer> element for the bottom part of the page.
    • On large viewports, the content is stretching horizontally thus making the images of the main content look larger as compared to the design. You could correct this by adding <div className="container"> element to wrap the different sections of the page and applying a max-width to it. In the design, the maximum width of the page is 1110px.
    • On the overall, the way you have approached the layout of the whole page with CSS grid is not bad but I think it lacks of flexibility and might be difficult to maintain in the long run. Here are 2 resources that will surely help you come up with a more efficient layout. Travis Horn - Responsive grid, CSS-TRICKS - Responsive Grid
    • In the main content, you are serving the desktop image for smaller viewports. In the design file, some of these images become in portrait mode on smaller viewports. You can have responsive images by using the <picture> and <source> elements in conjunction with an <img/> element to serve the right image on corresponding viewport widths. Read more here.
    • Please check the styling for the heading as some of them are not picking the correct font family.
    • The social icons look larger as compared to the design and the links to redirect the user to the respective social media website are missing.

    I hope this helps.

    Keep it up.

    1
  • @catherineisonline

    Submitted

    Hello, Frontend Mentor community! This is my solution to the REST Countries API with a color theme switcher.

    I appreciate all the feedback you left that helped me to improve this project. I fixed the issues I had previously and added new features per your recommendation.

    I have done this project a while ago however if you are here and have any feedback, tips, or ideas to share, I will be more than glad to hear them out!

    Thanks

    P

    @christopher-adolphe

    Posted

    Hi @catherineisonline,

    You did a great job in completing this challenge using React. ๐Ÿ‘

    • For your issue to display the border countries, I suggest you build a list of countries with their respective alpha codes on the initial load since you are already fetching all the countries. Something like this:
    const countryCodes: [
      {
        name: 'Albania',
        alpha2Code: 'AL',
        alpha3Code: 'ALB'
      },
      {
        name: 'Algeria',
        alpha2Code: 'DZ',
        alpha3Code: 'DZA'
      }
    ]
    

    Then you could store this list in a global state using React's context API and when a user goes to the country page you can find the country codes from this countryCodes list and map them to their respective full name.

    • It might also be a good thing to wrap asynchronous logics in a try...catch block to catch any potential errors.

    I hope this helps.

    Keep it up.

    Marked as helpful

    1
  • P

    @christopher-adolphe

    Posted

    Hello @Hannah-Ogunyinka,

    You did a good job on the overall for this challenge. ๐Ÿ‘ You have handle the website's responsiveness well.๐Ÿ‘Œ For the styling of the links, I wouldn't have thought of achieving this with a gradient. Well done! ๐Ÿ‘ Below are a few things I have noticed. You might want to check them in order to improve your solution.

    • I have viewed the solution on Chrome browser and the <h1> is not visible. At the moment, I see a white and black rectangle while on Safari and Firefox it is working fine. Maybe you should find a fallback that works on Chrome.
    • For the links Our location and Back to Home, please remove the <button> element from inside the <a> element and move the styles you have applied to the <a> element itself. It would be even better if you add a class like .btn which will have all these styles that you could then apply to these links.
    • Please do check the href attributes on the links as well because at the moment, I get a 404 error. Try to change <a href="/location.html"> to <a href="location.html">.
    • Lastly, if you use <h2> elements instead of <h3> in the <main> section, you could get rid of this accessibility warning.

    I'm currently tackling this challenge. I felt like I was cheating while doing this review ๐Ÿ˜‚

    I hope this helps.

    Keep it up.

    Marked as helpful

    0
  • Ollieโ€ข 580

    @ohermans1

    Submitted

    Hey Frontend Mentor folk

    I have completed another advanced challenge using my newfound Angular skills.

    Overall, I really enjoyed this project and didn't have any major issues - but I felt it really stretched my skills.

    I also added a little extra functionality, so that each photo on the story page can be clicked to open up more information about that photo/story - I didn't spend too long styling these extra pages, but let me know what you think.

    As always, I would really appreciate any feedback!

    Have an awesome day, happy coding!

    Regards

    Ollie

    EST Time 20h | Actual Time 16h | WakaTime

    P

    @christopher-adolphe

    Posted

    Hi @ohermans1,

    You did a good job for this challenge. ๐Ÿ‘ I like your responsive approach for the comparison table on the Pricing page. ๐Ÿ‘Œ I've seen very few solutions implemented with Angular over here, so I had a look at your github repository and here are a few things I have noticed and you want to check in order to improve your solution.

    While this is a fairly medium sized Angular project, it is important to organise your components' imports. I think that at the moment, the app.module.ts is quite bloated; all components are loaded while they are not all immediately used. You could leverage on feature modules to streamline this and at the same time you could also benefit from lazy-loading so that only the necessary modules are loaded when required. Below is how I would suggest refactoring this.

    • Create feature modules for Home, Stories, Features and Pricing with each having its routing configuration. For example:

    home.module.ts

    import { NgModule } from '@angular/core';
    
    import { SharedModule } from '../shared/shared.module';
    import { HomeRoutingModule } from './home-routing.module';
    import { HomeComponent } from './home.component';
    import { ContentComponent } from './content/content.component';
    
    @NgModule({
      declarations: [
        HomeComponent,
        ContentComponent
      ],
      imports: [
        SharedModule,  // <= Importing shared components via this shared module
        HomeRoutingModule
      ]
    })
    export class HomeModule { }
    

    home-routing.module.ts

    import { NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';
    
    import { HomeComponent } from './home.component';
    
    const routes: Routes = [
      {
        path: '',
        component: HomeComponent
      }
    ];
    
    @NgModule({
      imports: [RouterModule.forChild(routes)],
      exports: [RouterModule]
    })
    export class HomeRoutingModule { }
    
    • Lazy-load each module by configuring the app-routing.module.ts.

    app-routing.module.ts

    import { NgModule } from '@angular/core';
    import { RouterModule, Routes } from '@angular/router';
    
    const  routes: Routes = [
      {
        path: 'home',
        loadChildren: () => import('./home/home.module').then(m  =>  m.HomeModule)
      },
      {
        path: 'stories',
        loadChildren: () => import('./stories/stories.module').then(m  => m.StoriesModule)
      },
      {
        path: 'features',
        loadChildren: () => import('./features/features.module').then(m  => m.FeaturesModule)
      },
      {
        path: 'pricing',
        loadChildren: () => import('./pricing/pricing.module').then(m  =>  m.PricingModule)
      },
      {
        path: '',
        redirectTo: '',
        pathMatch: 'full'
      }
    ];
    
    @NgModule({
      imports: [RouterModule.forRoot(routes)],
      exports: [RouterModule]
    })
    export class AppRoutingModule { }
    
    • Move all the other shared components to a shared.module.ts that can be imported by the feature modules.

    shared.module.ts

    import { NgModule } from '@angular/core';
    import { CommonModule } from '@angular/common';
    
    import { FeaturesGroupComponent } from './features-group/features-group.component';
    import { GalleryComponent } from './gallery/gallery.component';
    import { ImageCardComponent } from './image-card/image-card.component';
    import { TopBannerComponent } from './UI/top-banner/top-banner.component';
    import { BottomBannerComponent } from './UI/bottom-banner/bottom-banner.component';
    
    @NgModule({
      declarations: [
        FeaturesGroupComponent,
        GalleryComponent,
        ImageCardComponent,
        TopBannerComponent,
        BottomBannerComponent
      ],
      imports: [
        CommonModule
      ],
      exports: [
        CommonModule,
        FeaturesGroupComponent,
        GalleryComponent,
        ImageCardComponent,
        TopBannerComponent,
        BottomBannerComponent
      ]
    })
    export class SharedModule { }
    
    • Then import only components that are required on initial load of the application in app.module.ts

    app.module.ts

    import { NgModule } from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    import { HeaderComponent } from './shared/UI/header/header.component';
    import { FooterComponent } from './shared/UI/footer/footer.component';
    
    @NgModule({
      declarations: [
        AppComponent,
        HeaderComponent,
        FooterComponent
      ],
      imports: [
        BrowserModule,
        AppRoutingModule
      ],
      providers: [],
      bootstrap: [AppComponent],
    })
    export class AppModule {}
    
    • I have noticed that you have reset your routing configurations to achieve the inner routes for the individual stories. Maybe you could consider using a child route where you pass an id and then get the story by id from the ImageService.

    stories-routing.module.ts

    import { NgModule } from  '@angular/core';
    import { RouterModule, Routes } from  '@angular/router';
    
    import { StoriesComponent } from  './stories.component';
    import { IndividualStoryComponent } from  './individual-story/individual-story.component';
    
    const routes: Routes = [
      {
        path: '',
        component: StoriesComponent,
        children: [
          {
            path: ':id',
            component: IndividualStoryComponent
          }
        ]
      }
    ];
    
    @NgModule({
      imports: [RouterModule.forChild(routes)],
      exports: [RouterModule]
    })
    export class StoriesRoutingModule { }
    
    • For the features table on the Pricing page, you may consider using ngTemplateOutlet and ngTemplateOutletContext to generate the rows of the table with the help of an *ngFor directive.
    ...
    <tbody class="table__body">
      <ng-container
        *ngFor="let feature of features"
        [ngTemplateOutlet]="featureItemTpl"
        [ngTemplateOutletContext]="{ featureItem: feature }"></ng-container>
    </tbody>
    ...
    
    <ng-template #featureItemTpl let-item="featureItem">
      <tr>
        <th>
          <h4>{{ item.title }}</h4>
        </th>
        <th>
          <img [src]="item.imgPath" alt="{{ item.title }}" />
        </th>
      </tr>
    </ng-template>
    

    If you wish to read more about feature/shared modules and lazy-loading, check the links below:

    I hope this helps.

    Keep it up.

    Marked as helpful

    1
  • Robin Del Mundoโ€ข 50

    @Robincredible

    Submitted

    Hello! This is my second attempt in doing stuff with React! This is another pretty simple project with an API call returning a JSON object.

    This free Advice Slip JSON API returns a random JSON response everytime it is called and I used fetch() to consume it and return the data. To generate a new advice per button click, I used the function Math.floor(Math.random * 20) to change the state with a new value everytime. At first I thought about using the function to return an ID, but since the API itself always returns a random object by default, I just have to update the state to rerender and call the API again.

    Feedback is very welcome!

    Questions for the community:

    • What did you find difficult while building the project?
    P

    @christopher-adolphe

    Posted

    Hi @Robincredible,

    You did a good job for this challenge. ๐Ÿ‘ I noticed that you have approached this React project with functional components. I really appreciate the animations you have added. ๐Ÿ‘Œ Below are a few things that I have noticed and you might want to check in order to improve your solution.

    • I think if you went with you initial thought of generating a random id to get the advice, it would have resulted in a simpler solution in terms of the number of states to manage. After checking the documentation of the adviceslip api, I saw that the random advice endpoint is cached. So you had to come up with a logic to ignore a click within that 2 seconds delay. By moving all the logic in the <Advice /> component itself, we get the following result.
    import React, { useEffect, useState, Fragment } from 'react';
    import mobileDivider from '../images/pattern-divider-mobile.svg';
    import divider from '../images/pattern-divider-desktop.svg';
    import dice from '../images/icon-dice.svg';
    
    const Advice = () => {
      const [ slip, setSlip ] = useState(null);
      const [ isLoading, setIsLoading ] = useState(false);
      const [ error, setError ] = useState(null);
      let adviceContent;
    
      const getSlip = async () => {
        const randomId = Math.floor(Math.random() * 200) + 1;
    
        try {
          setIsLoading(true);
          const response = await fetch(`https://api.adviceslip.com/advice/${randomId}`);
    
          if (response.ok && response.status === 200) {
            const data = await response.json();
    
            if (data.hasOwnProperty('slip')) {
              setSlip(data.slip);
            } else {
              setError(data.message.text);
              throw new Error(data.message.text);
            }
          } else {
            const errorData = await response.json();
            throw new Error(errorData);
          }
        } catch (error) {
          setError(error.message);
        } finally {
          setIsLoading(false);
        }
      };
    
      useEffect(() => {
        getSlip();
      }, []);
    
      if (isLoading) {
        adviceContent = (
          <Fragment>
            <h1>Loading</h1>
            <Loader />
          </Fragment>
        );
      } else if (error) {
        adviceContent = (
          <Fragment>
            <h1>Sorry</h1>
            <p>{ error }</p>
          </Fragment>
        );
      } else {
        adviceContent = (
          <Fragment>
            <h1>{ `Advice #${slip?.id}` }</h1>
            <p>{ slip?.advice }</p>
          </Fragment>
        );
      }
    
      return (
        <div className="wrapper">
          <div className="loader-wrapper">
            <div className="loader noselect"></div>
          </div>
          <div className="advice-wrapper">
            <div className="quote">{ adviceContent }</div>
    
            <div className="divider noselect">
              <img src={mobileDivider} srcSet={`${mobileDivider} 550w, ${divider} 1920w`}  sizes="(max-width: 600px) 550px,
                1920px" alt=""/>
            </div>
          </div>
          <button type="button" className="random-quote-button noselect" onClick={ getSlip }>
            <img src={dice} alt="Random Quote" />
          </button>
        </div>
      );
    };
    
    //pure CSS animation from https://codepen.io/sudeepgumaste/pen/abdrorB
    const Loader = () => {
      return(
        <div className="loading-box">
          <div className="loading-container">
            <span className="circle"></span>
            <span className="circle"></span>
            <span className="circle"></span>
            <span className="circle"></span>
          </div>
        </div>
      )
    }
    
    export default Advice;
    

    I think by doing so, prevents you from scattering the logic to achieve a goal into several components which can unnecessarily complicate things.

    Ideally, the <Loader /> component would have been in a separate file as well ๐Ÿ˜‰

    • It is also a good practice to catch potential errors when dealing with asynchronous operations. In the proposed refactored <Advice /> component, I have used a try...catch block with async...await but you could do same if prefer the promise approach.

    I hope this helps.

    Keep it up.

    1
  • Robin Del Mundoโ€ข 50

    @Robincredible

    Submitted

    Hello! This is my first time using react so this project took a while even though it was very simple! I had to solve some quirks in react that I hadn't normally dealt with on vanilla JS, like passing props and managing state properly. I quickly realized that you really have to deal with structure when using react because of how it's designed.

    Feedback is very welcome as I am also a beginner! And I would very much like to learn from more experienced individuals in here about the best practices in frontend engineering!

    Questions for the community:

    • When you built the project, what aspects of it did you find difflicult?
    • What tools did you use?
    P

    @christopher-adolphe

    Posted

    Hi @Robincredible,

    You did a great job for this challenge. ๐Ÿ‘ I really like the subtle animation when a rating is selected and also the notification appears. I also like your approach to get the selected value via an <input type="hidden"> element. It's been while since I have used the <input type="hidden"> and I almost forgot about it until I saw your code. ๐Ÿ˜… I would have used <input type="radio"> but this is also a nice alternative. I tried to answer your questions below.

    • React (as well as Angular, Vue, etc.) is component-based and at the beginning, it can be difficult to determine what parts of the design should be a component on its own or not. Splitting every single part of the design into individual components can easily complicate things like managing state and events. I've set myself a rule where as soon as I see that things are getting too complicated, I take a few steps back and take a few minutes to think of simpler alternatives. For example if I was to tackle this challenge, I would have most probably created only 2 components; a generic <Card /> component and a <RatingForm /> component. The end result would have looked like this:
    <div className="App">
        <div className="container">
            <Card>
                <RatingForm />
            </Card>
        </div>
    </div>
    

    If you wish to make the <RatingForm /> component more reusable, you could have it accept props for the title and the text like <RatingForm title="Some title" text="Some text" />. In short, more components does not always equal better reusability. Just keep things simple.

    • It is also a good practice to separate components in individual files as it promotes separation of concerns, better readability and eases maintenance as your app grows. I understand this is a small project but you can start right now to build up the good habits. ๐Ÿ˜‰
    • In the RatingsContainer component, I noticed there is an onClick props on the div className='ratings-main-container' onClick={this.handleClick} > while this.handleClick is not set. Why is that ? ๐Ÿค”
    • Regarding the tools to use, I think it's just a matter of preference. For simple challenges, I tend to use just HTML, CSS and JS and as the complexity rises, I could use any of React, Angular or Vue. I just see these challenges as an opportunity to become more familiar with those tools.

    I was also surprised to see that you have used class-based component, this is less common nowadays.

    I am also new to React and I've tried to share with you what I've learnt so far. I hope this helps.

    Keep it up.

    Marked as helpful

    1
  • @Jeth0214

    Submitted

    Hello Guys, Is it good to have this project as my portfolio? I am just going to change those images and edit the descriptions and links. Can you guys give ideas on what I need to improve or change if I will going to use it as my portfolio.? Also, please have a code review on my solution. It will help me write my code more efficient and readable. Thanks.

    Minimalist Portfolio using HTML, CSS, Javascript

    #accessibility#fetch#sass/scss#semantic-ui#solid-js

    3

    P

    @christopher-adolphe

    Posted

    Hi @Jeth0214,

    You did a great job for this challenge. ๐Ÿ‘ I have check your solution from your git repository and everything is nice and neat there. ๐Ÿ‘Œ I like the approach you have used for the portfolio. I only have the following suggestions.

    • You should refrain from using id selectors in your CSS because it hinders on the reusability of style.
    • I saw that you are handling responsive images via JavaScript. I recently found that there is nice alternative to achieve the same result. Maybe you can give it a try. ๐Ÿ˜‰ Read more here
    <picture>
      <source media="(max-width: 799px)" srcset="elva-480w-close-portrait.jpg">
      <source media="(min-width: 800px)" srcset="elva-800w.jpg">
      <img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva">
    </picture>
    
    • You might want to wrap asynchronous logics in a try...catch block like this:
    async function renderProjectsToTheView(width) {
        try {
            let response = await fetch("../assets/json/projects.json");
    
            if (response.status === 200) {
                data = await response.text();
            } else {
                throw new Error(`Error: ${response.statusText}`);
            }
    
            projects = JSON.parse(data);
            device = checkDevice(width);
            getProjectsData(projects, device);
        } catch (error) {
            console.log(error);
        }
    }
    

    And yes, you can absolutely use this as your portfolio website but I would suggest you to spice it up a little bit more so that you come up with something unique because I think many have had the same idea of using this project as their own portfolio. ๐Ÿ˜‰

    I hope this helps.

    Keep it up.

    Marked as helpful

    2
  • Kylee Bustamanteโ€ข 90

    @kyleebustamante

    Submitted

    I found it difficult to position the social icons so any feedback on how to do that better would be helpful. I'm also getting overflow on the <body> section that I couldn't resolve. Thanks in advance for checking out my solution!

    P

    @christopher-adolphe

    Posted

    Hi @kyleebustamante,

    Well done for this challenge ๐Ÿ‘. Below are a few things that I noticed and you might want to check in order to improve your solution.

    • To correct the horizontal overflow you mentioned, simply apply the following CSS rule in your stylesheet:
    html,
    body {
      margin: 0;
    }
    

    By default, all browsers apply some user agent styles to html elements which can sometimes differ from browser to browser. It is therefore a good practice to normalize these user agent styles so that your website gets a uniform default styling on all browsers. You can read more here

    • On larger viewports the content is expanding all the way horizontally. I would suggest you to apply a max-with: 1440px; to the <div class="container">. You can then use this <div class="container"> element inside your <header> and <footer> elements as well to keep everything consistent. You should end up with an html structure like this:

    html:

    ...
    <header>
      <div class="container">...</div>
    </header>
    
    <main>
      <div class="container">...</div>
    </main>
    
    <footer>
      <div class="container">...</div>
    </footer>
    ...
    

    CSS:

    .container {
      display: flex;
      justify-content: space-around;
      flex-direction: row;
      width: 100%;
      max-width: 1440px;
      margin: 0 auto;
    }
    
    • On certain viewports, the social icon's shape is getting oval. You could easily solve this by refactoring the styles like this:
    .social {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    .social a {
      display: flex;
      justify-content: center;
      align-items: center;
      width: 42px;
      height: 42px;
      border: 1px solid #ffffff;
      border-radius: 50%;
    }
    
    .social a:not(:last-child) {
        margin-right: 12px;
    }
    
    .social i {
        color: #ffffff;
        font-size: 1.25rem;
    }
    

    I hope this helps.

    Keep it up.

    Marked as helpful

    1