Skip to content
  • Unlock Pro
  • Log in with GitHub
Profile
OverviewSolutions
25
Comments
25
P
Habeeb Kareem
@olaide-hok

All comments

  • P
    Nishanth Venkatesan•1,030
    @nishanth1596
    Submitted 2 months ago
    What are you most proud of, and what would you do differently next time?

    This was my first time using Framer Motion, and I implemented it to add a smooth sliding transition to the accordion component.

    Recently, I learned about the compound components pattern, but I initially struggled to understand it. Since the best way to learn is by doing, I decided to apply it in this project. Although I faced challenges at first, I was able to complete it—and now, I feel like I have a solid understanding of the pattern, which I'm proud of.

    What specific areas of your project would you like help with?

    I need help with adding the background image and text decoration (underline).

    Currently, the width of the underline remains constant instead of adjusting dynamically based on the content. I’d appreciate any help in resolving these two issues.

    Responsive bookmark landing page

    #framer-motion#react#react-hook-form#typescript#tailwind-css
    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 2 months ago

    Hi there,

    Great work here!

    Well done.

    Regarding the width of the underline remaining constant instead of adjusting dynamically based on the content. An approach would be to style using ::after pseudo css selector. The width will then be in percentage instead of a fixed value.

    For the background image, two approaches

    • add it using the ::before css pseudo selector.
    • add it by adding and styling a div as a sibling element of the image. With the parent div position relatively and target div absolutely. The width and height would have percentage values in other to be responsive.

    For example

    function HeroSection() {
      return (
        <section className="mx-8 mt-20 lg:mx-12 lg:mt-[6.75rem] lg:grid lg:grid-cols-2 xl:mx-0 xl:gap-16">
    <div class="relative">
          <img
            className="mx-auto lg:order-2"
            src={heroImg}
            alt="Illustration of a browser tab showing a list of bookmarks with colored indicators"
          />
    <div class="pos right-0 bottom-0 -z-1 bg-[#5267D} w-[75%] h-[75%] rounded-l-[999px]"></div>
    </div>
    
    // Article section goes here
        </section>
      );
    }
    

    Or styling the pseudo

    function HeroSection() {
      return (
        <section className="mx-8 mt-20 lg:mx-12 lg:mt-[6.75rem] lg:grid lg:grid-cols-2 xl:mx-0 xl:gap-16">
    <div class="hero-img-wrapper relative">
          <img
            className="mx-auto lg:order-2"
            src={heroImg}
            alt="Illustration of a browser tab showing a list of bookmarks with colored indicators"
          />
    </div>
    
    // Article section goes here
        </section>
      );
    }
    
    .hero-img-wrapper::before{
        content: "",
        background: #5267DF;
        position: absolute;
        width: 75%;
        height: 75%;
    }
    
    

    NB all tailwind utility classes and css rules provided have to be edited to suite specific needs.

    The Feature Section component could be update to

    
    function HeroSection() {
      return (
        <section className="mx-8 mt-20 lg:mx-12 lg:mt-[6.75rem] lg:grid lg:grid-cols-2 xl:mx-0 xl:gap-16">
        <article>
               <nav className="mt-8" aria-label="Download options">
              <ul className="flex items-center justify-left gap-3.5">
                // li tags goes here
              </ul>
            </nav>
          </article>
        </section>
      );
    }
    

    Well done and happy coding.

  • P
    toshirokubota•1,200
    @toshirokubota
    Submitted 2 months ago
    What challenges did you encounter, and how did you overcome them?

    It was tricky to align the arrows for the two screen sizes. I was able to do it with subgrid. This was my first attempt to implement a carousel, and I struggled at first. It should be easier the next time around.

    What specific areas of your project would you like help with?

    Any sort of feedbacks are highly appreciated! If you could do the layout in a simpler way, I would like to learn about it.

    room-homepage

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 2 months ago

    Great work here!

    Your JS contained

    ✅ Readable Code

    • The code is well-structured with meaningful variable names (carousel_pics, carousel_txts, etc.).
    • The use of function encapsulation (e.g., updateCarousel(), carouselNext(), showMenu()) improves clarity.

    ✅ Accessibility Considerations

    • The use of tabIndex, aria-hidden, and keydown event listeners for keyboard navigation enhances usability for screen readers.

    ✅ Separation of Concerns

    • The carousel logic and navigation logic are separated into different sections, making the code easier to manage.

    Some areas for Improvement includes

    🔹 1. Issue with captions.forEach(e => { e.shop_now = e.querySelector('.shop-now'); });

    Problem:

    • shop_now is being assigned as a new property to e (a DOM element), which is not a standard practice and could lead to unexpected behavior.

    Suggested Fix:
    Use a Map or a dataset attribute instead of modifying the DOM object:

    const shopNowButtons = new Map();
    captions.forEach(e => {
        shopNowButtons.set(e, e.querySelector('.shop-now'));
    });
    

    Then, access it like this:

    shopNowButtons.get(txt).tabIndex = 0;
    

    🔹 2. Inefficient updateCarousel() Function

    Problem:

    • The function recalculates img_width and caption_width every time it runs, which is unnecessary.

    • Instead of looping through all images and captions every time, we can optimize how we update styles.

    Suggested Fix: Cache widths once and reuse them like below:

    const imgWidth = pictures[0].clientWidth;
    const captionWidth = captions[0].clientWidth;
    
    const updateCarousel = (count) => {
        carousel_pics.style.transform = `translateX(-${count * imgWidth}px)`;
        carousel_txts.style.transform = `translateX(-${count * captionWidth}px)`;
    
        pictures.forEach((pic, i) => {
            const txt = captions[i];
            const isActive = i === count;
            pic.setAttribute('aria-hidden', !isActive);
            txt.setAttribute('aria-hidden', !isActive);
            shopNowButtons.get(txt).tabIndex = isActive ? 0 : -1;
        });
    };
    
    • This reduces unnecessary calculations and improves performance.

    🔹 3. Event Listeners Could Be More Efficient

    Problem:

    • Multiple click and keydown event listeners for each button could be refactored into a single handler.

    • Using e.key === "Enter" is preferred over e.code == 'Enter' for better compatibility.

    Suggested Fix: Use a single function for event handling:

    const handleKeyEvent = (event, action) => {
        if (event.type === "click" || event.key === "Enter") {
            action();
        }
    };
    
    left_arrow.addEventListener("click", carouselPrev);
    left_arrow.addEventListener("keydown", (e) => handleKeyEvent(e, carouselPrev));
    right_arrow.addEventListener("click", carouselNext);
    right_arrow.addEventListener("keydown", (e) => handleKeyEvent(e, carouselNext));
    
    

    On the CSS side,

    1. In desktop viewport you can apply a width: 1440px to the main tag. This would keep the content in place for viewport greater than 1440px.
    2. You can still refactor the section tag with about class to a grid-template-column: repeat(3, 1fr); for desktop viewport and grid-template-column: 1fr; for mobile. The padding could be adjusted for mobile and desktop also.

    Overall, well done. Happy coding!

    Marked as helpful
  • P
    toshirokubota•1,200
    @toshirokubota
    Submitted 3 months ago
    What challenges did you encounter, and how did you overcome them?

    This is my first attempt with SCSS/SASS and I found it fairly straightforward. Since it makes it easier to write the stylesheet, I feel that I might have become less mindful on clarity, organization, and simplicity on the design. I wanted to use BEM on this project but I am still new to BEM and was not patient enough to stick through the naming convention. Hopefully, on the next project, I will be able to stick through with it.

    What specific areas of your project would you like help with?

    This is my first use of SCSS/SASS. It makes it easier to write the style sheet, but I may be making more verbose and less concise because of that. Any advices/comments/suggestions on my use of SCSS/SASS are highly appreciated.

    loopstudios-landing-page

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 2 months ago

    Great job here.

    Your SASS did show consistent variables, typography mixin, responsive design, BEM-like structure, and accessibility.

    Some areas to improve on would be to consider

    1. Typography Mixin Overuse: The mixin is used for small, one-off styles (e.g., font-size: 14px), which adds unnecessary complexity. You can reserve the mixin for repeated typography patterns (e.g., headings) and use direct properties for one-offs. For example
      font-size: 14px;  // Direct property
      @include typography(14px, 'Alata', 400, 14px, 5px);  // Only if reused
    }
    
    1. Nesting Depth and Specificity: Over-nesting (e.g., header nav .menu) can lead to high specificity and hard-to-maintain code. You can limit nesting to 3 levels and use BEM-like classes for scalability for this particular project. For example:
    .header {
      &__nav {
        padding: 0 1rem;
      }
      &__menu {
        background: $color-black;
      }
    }
    
    1. For the hover and focus state on links, you can consider using the ::after pseudo selector and then style it to meet the design. This would give you room to adjust the width dynamically without affecting the text.
    Marked as helpful
  • Leonardo Sauberman•50
    @leosauberman
    Submitted 2 months ago

    NFT Preview Card

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 2 months ago

    Great job here!

    A change you can consider is changing the cursor to pointer on hovering over the image.

    .card .image-view:hover .overlay {
      opacity: 1;
      pointer-events: auto;
      cursor: pointer;
    
    }
    

    Happy coding!

  • P
    Jair•700
    @JairRaid
    Submitted 3 months ago

    tic tac toe react

    2
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 2 months ago

    Great work on the UI.

    Considering the coming soon banner you added, I'm assuming you are still working on the project.

    Happy coding!

    Marked as helpful
  • P
    Jair•700
    @JairRaid
    Submitted 3 months ago

    body mass index calculator

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 3 months ago

    Great work here!

    Some areas for improvements would be:

    1. Readability: Some variable and function names are not descriptive enough (e.g., mVal, value in setBMIResultDOM). You can use more descriptive names to improve readability. Examples would be renaming mVal to heightInMeters, value in setBMIResultDOM to isEmpty.

    2. Reusability: The code is tightly coupled to the DOM structure, making it less reusable. An approach would be to decouple the logic from the DOM by passing elements as parameters or using dependency injection. For example

    function calculateBMI(unitSystem, height, weight, heightInches = 0, weightLbs = 0) {
      // BMI calculation logic
    }
    
    1. Duplicate Code: Some logic is repeated (e.g., setBMIResultDOM is called in multiple places). You can consolidate repeated logic into reusable functions. For example
    function updateResultUI(bmi, classification) {
      this.setBMIResultDOM(false);
      this.bmiScore.textContent = bmi.toFixed(1);
      this.updateBMITextResult(classification);
    }
    
    1. Imperial Unit Support: The code only calculates BMI for metric units. Imperial unit support is incomplete. You can add functions to calculate BMI for imperial units. Example:
    function calculateImperialBMI(ft, inches, st, lbs) {
      const heightInInches = ft * 12 + inches;
      const heightInMeters = heightInInches * 0.0254;
      const weightInKg = (st * 14 + lbs) * 0.453592;
      return weightInKg / (heightInMeters * heightInMeters);
    }
    
    1. Performance: The keyup event listener recalculates BMI on every keystroke, which can be inefficient. Consider using the debouncing approach to limit the number of calculations.

    Example:

    function debounce(func, delay) {
      let timeout;
      return function () {
        clearTimeout(timeout);
        timeout = setTimeout(() => func.apply(this, arguments), delay);
      };
    }
    
    inputsContainer.addEventListener("keyup", debounce(() => {
      if (units === "metric") {
        if (canCalculateMetricBMI()) {
          updateResultUI();
        }
      }
    }, 300));
    

    Overall, well done. Happy coding!

  • P
    Jair•700
    @JairRaid
    Submitted 3 months ago

    E commerce product page

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 3 months ago

    Great work here!

    The UI looks really good.

    Some areas to review would be:

    1. Repeated Code: There is repeated code for deselecting thumbnails and updating the carousel slide. You can create reusable functions for common tasks. For example
    function deselectAllThumbnails(container) {
        for (const image of Object.values(container.children)) {
            image.classList.remove("thumbnail-selected");
        }
    }
    
    // Usage
    deselectAllThumbnails(thumbnailEl);
    deselectAllThumbnails(secondThumbnailEl);
    
    1. Accessibility: The code does not consider accessibility (e.g., keyboard navigation, ARIA attributes). You can add ARIA attributes and ensure all interactive elements are keyboard accessible.

    For example:

    nextBtn.setAttribute("aria-label", "Next slide");
    prevBtn.setAttribute("aria-label", "Previous slide");
    
    1. Performance Optimization: The resizeHandler function is called frequently during window resizing, which can cause performance issues.You can use debouncing to limit the number of times the function is called. E.g
    function debounce(func, delay) {
        let timeout;
        return function () {
            clearTimeout(timeout);
            timeout = setTimeout(() => func.apply(this, arguments), delay);
        };
    }
    
    window.addEventListener("resize", debounce(resizeHandler, 100));
    
    
    1. Code Comments: Some parts of the code lack comments, making it harder to understand the purpose of certain logic. Consider adding comments to explain complex or non-obvious logic.

    Overall well done!

    Marked as helpful
  • Fernando Batista•630
    @FernJBatista
    Submitted 3 months ago

    News homepage - HTML5 , SCSS, Vanilla JS, Mobile First.

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 3 months ago

    Great work!

    Some areas to improve on are:

    • there is an overflow on mobile view.

    • CSS Reset: the reset is overly verbose and includes many elements that may not be necessary. Consider using a more concise reset like Normalize.css or Modern CSS Reset. Alternatively, use a tool like PostCSS to automate resetting only the elements you use. I personally use Normalize.css.

    • Repetitive Media Queries: the media queries are repeated for similar breakpoints (e.g. $tablet-breakpoint, $desktop-breakpoint). This can be streamlined using mixins or utility classes. You could create a mixin for common breakpoints for example

    @mixin for-tablet {
        @media screen and (min-width: $tablet-breakpoint) {
            @content;
        }
    }
    
    @mixin for-desktop {
        @media screen and (min-width: $desktop-breakpoint) {
            @content;
        }
    }
    
    @mixin for-big-screen {
        @media screen and (min-width: $big-screen-breakpoint) {
            @content;
        }
    }
    

    and you could use like

    header {
        padding: 2rem 1rem 1rem;
        max-width: $tablet-breakpoint;
    
        @include for-tablet {
            max-width: $desktop-breakpoint;
        }
    
        @include for-desktop {
            max-width: $big-screen-breakpoint;
            padding: 2rem 2rem;
        }
    }
    
    • Lack of Utility Classes: there are repeated styles (e.g., display: flex, gap: 1rem) could be abstracted into utility classes for reusability. You could create utility classes such as:
    .flex {
        display: flex;
    }
    
    .flex-col {
        flex-direction: column;
    }
    
    .gap-1 {
        gap: 1rem;
    }
    
    .gap-2 {
        gap: 2rem;
    }
    

    and usage would be:

    #newsContainer {
        @extend .flex, .flex-col, .gap-1;
    }
    
    • the font-size for the h1 max-value is 56px, the clamp values can be updated.

    • the spacings as well could be updated for large screens.

    Overall, great work, well done.

  • abdulrrahmann•270
    @abdulrrahmann
    Submitted 3 months ago

    Contact Form

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 3 months ago

    Great job here!

    Some areas to improve the UI are:

    1. The checkbox and radio input type color can be updated by adding css rule to the selector below:
    .container form .query-control .radio-group input {
    accent-color: #0c7d69;
    }
    
    1. The radio-group can be wrapped in a single div and given a display of flex with a flex-direction set to column for mobile and on tablet and above viewport, it is then set to row. This approach can be utilized for the first and last name input fields as well.

    For your JS:

    1. Code Duplication: There is repeated code for adding/removing the error class and toggling error messages. This can be refactored into reusable functions. You can create helper functions for adding/removing errors:
    const showError = (element, errorElement) => {
      element.classList.add("error");
      errorElement.classList.remove("d-none");
    };
    
    const hideError = (element, errorElement) => {
      element.classList.remove("error");
      errorElement.classList.add("d-none");
    };
    

    Use these functions to simplify the validation logic:

    if (!fName.value) {
      showError(fName, document.querySelector(`#fname + span`));
      success = false;
    }
    
    1. Email Validation: The email validation regex (/^\w+@[a-zA-Z_]+.[a-zA-Z]{2,3}$/) is too restrictive. It doesn’t account for valid email formats like user.name+tag@domain.co.uk. You can use a more robust regex for email validation:
    const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
    
    1. Error Message Handling: The code does not clear all errors when the form is reset or when the user starts typing again. You can create a function to clear all errors such as:
    const clearErrors = () => {
      const errorElements = document.querySelectorAll(".error-message");
      errorElements.forEach((element) => element.classList.add("d-none"));
    
      const inputElements = document.querySelectorAll("input, textarea");
      inputElements.forEach((element) => element.classList.remove("error"));
    };
    

    Then call this function in the change event listener:

    myForm.addEventListener("change", clearErrors);
    

    Overall, well done!

    Marked as helpful
  • Fernando Batista•630
    @FernJBatista
    Submitted over 1 year ago

    FAQ Accordion - CSS, Flexbox, JavaScript

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 3 months ago

    Well done!

    Some areas to improve would include updating the css of some selectors:

    .accordion {
    	font-family: Work Sans;
    }
    
    section {
        border-bottom: 1px solid var(--Light-Pink);
    }
    
    section .accordion:hover {
        color: #AD28EB;
    }
    
    section p {
        color: #8B6990;
    }
    
    

    On the JS side,

    1. Performance: Your code uses document.getElementsByClassName, which returns a live HTMLCollection. This can cause performance issues if the DOM changes frequently. Use document.querySelectorAll instead, which returns a static NodeList:
    let acc = document.querySelectorAll(".accordion");
    
    1. Avoid Inline Styles: The code directly manipulates the style.display property of the panel. This approach is not ideal because it mixes JavaScript with presentation logic, making it harder to maintain and override styles with CSS. Consider using CSS classes to control visibility and transitions.

    For example:

    .panel {
      display: none;
      transition: max-height 0.3s ease-out;
      overflow: hidden;
      max-height: 0;
    }
    
    .panel.active {
      display: block;
    }
    
    

    Update the JavaScript to toggle the .active class:

    if (panel.classList.contains("active")) {
        panel.classList.remove("active");
        icon.src = "./CSS/images/icon-plus.svg";
        icon.alt = "Open-FAQ-Icon";
    } else {
        panel.classList.add("active");
        icon.src = "./CSS/images/icon-minus.svg";
        icon.alt = "Close-FAQ-Icon";
    }
    
    Marked as helpful
  • Fernando Batista•630
    @FernJBatista
    Submitted 3 months ago

    Interactive Ratign Componenet - SCSS, Mobile first.

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 3 months ago

    Great work here!

    Some feedbacks:

    1. Redundant Event Listener: The body click event listener is added every time the form is submitted, which can lead to multiple listeners being attached unnecessarily. This should be added only once, outside the submit event listener.
    const body = document.querySelector('body');
    body.addEventListener('click', function(event) {
        if (event.target.id === 'main' || event.target.id === 'successMessage') {
            successMessage.classList.remove('active');
            form.classList.add('active');
        }
    });
    
    
    1. Default Value Logic: The default value of ratingValue is set to 5, but this assumes that the user will always select a rating. If no rating is selected, the code should handle this case explicitly.
    let ratingValue = null; // Default to null or undefined
    ratingOptions.forEach(option => {
        if (option.checked) {
            ratingValue = option.value;
            console.log("This is the rating selected: " + ratingValue);
        }
    });
    
    if (ratingValue === null) {
        alert("Please select a rating before submitting.");
        return; // Exit the function early
    }
    
    
  • P
    toshirokubota•1,200
    @toshirokubota
    Submitted 3 months ago
    What challenges did you encounter, and how did you overcome them?

    I found the followings challenging: converting a checkbox into a sliding switch and customizing a progress bar with different colors. I was able to find solutions via web search and consulting with an AI chatbot.

    What specific areas of your project would you like help with?

    Any suggestions and comments are highly appreciated. If you see any glitches, please let me know.

    frontend quiz app

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 3 months ago

    Great work here!

    Some UI improvements would be adding the extra css rules

    label > .result-icon {
    align-self: start;
    }
    
    body.light-theme .strong-bg:hover {
        border: 3px solid var(--purple);
    }
    
    body.light-theme progress {
      -webkit-appearance: none;
    }
    
    body.light-theme progress::-webkit-progress-bar {
      background-color: var(--white);
    }
    

    You can check here on how to style a progress element.

    For the JS side, some areas of improvemnet would be

    1. Code Duplication: There is some duplication in the code, such as clearing and setting classes for category_areas. You can refactor repeated logic into reusable functions such as
    const resetCategoryAreas = () => {
      category_areas.forEach((area) => {
        const icon = area.querySelector('.category-icon');
        icon.className = 'category-icon'; // Reset classes
        area.querySelector('.category-name').textContent = '';
      });
    };
    
    const setCategoryAreas = (category) => {
      category_areas.forEach((area) => {
        const icon = area.querySelector('.category-icon');
        icon.className = `category-icon ${category.toLowerCase()}-icon`;
        area.querySelector('.category-name').textContent = category;
      });
    };
    
    1. Event Listener Organization: Event listeners are scattered throughout the code, making it harder to track and maintain.You can group related event listeners together and consider using event delegation where applicable.
    // Group event listeners
    const setupEventListeners = () => {
      // Form submission
      form.addEventListener('submit', handleFormSubmit);
    
      // Start game buttons
      document.querySelectorAll('#start-page button').forEach((button) => {
        button.addEventListener('click', () => startGame(button.textContent));
      });
    
      // Radio button selection
      form.addEventListener('change', (e) => {
        if (e.target.matches('input[type="radio"]')) {
          handleAnswerSelection(e.target);
        }
      });
    
      // Play again button
      play_again.addEventListener('click', showInitialPage);
    
      // Theme switch
      theme_switch.addEventListener('click', toggleTheme);
    };
    
    // Call setupEventListeners at the end of the script
    setupEventListeners();
    
    
    1. Use of const and let: Some variables that don’t change are declared with let instead of const. Use const for variables that are not reassigned.
    const form = document.getElementById('form');
    const startPage = document.getElementById('start-page');
    const playPage = document.getElementById('play-page');
    const resultPage = document.getElementById('result-page');
    const body = document.querySelector('body');
    
    1. Theme Initialization: The theme initialization logic is verbose and could be simplified. Consider using a ternary operator for cleaner code.
    const savedTheme = localStorage.getItem('theme');
    if (savedTheme) {
      body.classList.toggle('light-theme', savedTheme === 'light');
      body.classList.toggle('dark-theme', savedTheme === 'dark');
      theme_switch.checked = savedTheme === 'dark';
    }
    
    

    Well done!

    Marked as helpful
  • P
    Jair•700
    @JairRaid
    Submitted 4 months ago

    password generator app

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 4 months ago

    Great work here! Some areas for improvement are

    1. The checkBoxesStates array and checkBoxes event listeners contain repetitive code for handling checkbox states. A loop or a more dynamic approach can be used to handle checkbox states.
    const checkBoxes = document.querySelectorAll("input[type='checkbox']");
    const checkBoxesStates = Array.from(checkBoxes).map((el) => ({
      category: el.name,
      isChecked: false,
    }));
    
    checkBoxes.forEach((el, index) => {
      el.addEventListener("click", (event) => {
        checkBoxesStates[index].isChecked = event.target.checked;
        checkLevelStrength();
      });
    });
    
    1. Hardcoded Values: The levelStates array and checkLevelState function use hardcoded values, making the code less flexible. Use a more dynamic approach to determine the strength level.
    const strengthLevels = [
      { level: 1, state: "TOO WEAK!", class: "too-weak" },
      { level: 2, state: "WEAK", class: "weak" },
      { level: 3, state: "MEDIUM", class: "medium" },
      { level: 4, state: "STRONG", class: "strong" },
    ];
    
    function checkLevelState() {
      if (passLength < 4 || passLevel === 0) {
        passStrength = "";
        return;
      }
    
      const level = strengthLevels.find((state) => state.level === passLevel);
      passStrength = level ? level.state : "";
    }
    
    1. Lack of Comments: The code lacks comments, making it harder for others (or your future self) to understand. Add comments to explain the purpose of each function and complex logic.

    2. Accessibility Improvements: The alt attribute for the copy icon (icon-copy.svg) is generic and doesn’t describe its purpose. The alt attribute for the arrow icon (icon-arrow-right.svg) is also generic. Use descriptive alt text for icons.

    <img src="./assets/images/icon-copy.svg" alt="Copy password to clipboard" id="copy">
    <img src="./assets/images/icon-arrow-right.svg" alt="Generate password" id="generate-icon">
    
    1. Missing for Attributes in Labels: The <label> elements do not have for attributes, which explicitly associate them with their respective inputs. Add for attributes to <label> elements.
    <label for="uppercase"><input type="checkbox" id="uppercase" name="uppercase">Include Uppercase Letters</label>
    <label for="lowercase"><input type="checkbox" id="lowercase" name="lowercase">Include Lowercase Letters</label>
    <label for="numbers"><input type="checkbox" id="numbers" name="numbers">Include Numbers</label>
    <label for="symbols"><input type="checkbox" id="symbols" name="symbols">Include Symbols</label>
    
    1. Typo in "STRENGHT" <p class="strength-text">STRENGTH</p>
    Marked as helpful
  • Tonny Blair•470
    @Tonny-Blair-Daniel
    Submitted 4 months ago

    Tip Calculator App

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 4 months ago

    Great work!

    Here are some quick areas to improve on in the html layout

    <div class="tip">
      <div class="inputContainer">
        <label for="bill">Bill</label>
        <input type="number" id="bill" class="bill" placeholder="0">
      </div>
      <div>
        <p>Select Tip %</p>
        <div class="percent">
          <button class="Btn" data-option="0.05">5%</button>
          <button class="Btn" data-option="0.10">10%</button>
          <button class="Btn" data-option="0.15">15%</button>
          <button class="Btn" data-option="0.20">20%</button>
          <button class="Btn" data-option="0.25">25%</button>
          <input type="number" class="custom" placeholder="custom">
        </div>
      </div> 
    
      <div class="inputContainer">
        <label for="numberOfPeople">Number of People</label><br>
        <input type="number" id="numberOfPeople" class="numberOfPeople" placeholder="0">
      </div>
    </div>
    

    for css

    .bill, .custom, .numberOfPeople {
        border-radius: 5px;
        background: #F3F9FA;
        padding: 6px 17px;
    }
    @media (min-width: 768px) {
        .tip {
            row-gap: 40px;
    }
    
    @media (min-width: 768px) {
        .content {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            width: 700px;
            border-radius: 10px;
            padding: 32px 32px 32px 48px;
        }
    
    .result {
       display: flex;
       flex-direction: column;
       justify-content: space-between;
      }
    
    }
    
  • Hendrixx•430
    @BeeAlmighty
    Submitted 4 months ago
    What are you most proud of, and what would you do differently next time?

    This project was quite challenging for me and i'm just glad to be able to complete it to the best of my ability.

    What specific areas of your project would you like help with?
    • I always look for alternative approaches and best practices to the way i handled my project.

    TIME-TRACKING-DASHBOARD HTML | CSS | JS

    2
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 4 months ago

    Job well done here!

    Some areas to improve on would be to set the color on these class to white.

    .profile--name h2, .duration h3, .active {
        color: #ffffff;
    }
    
    <section class="profile--timeframe">
            <button type="button" class="daily">Daily</button>
            <button type="button" class="weekly active">Weekly</button>
            <button type="button" class="monthly">Monthly</button>
    </section>
    

    The Daily, Weekly and Monthly texts are meant to be interactive elements hence rendering them has buttons would be much better for screen readers to pick them us as such. They can then be styled properly.

    For the javascript side, there exist some repetitive code. The event listeners for daily, weekly, and monthly contain repetitive code. For example, the logic for adding/removing the active class and calling getData is duplicated.

    A way to tackle this would be to create a reusable function to handle the common logic.

    const setTimeframe = (timeframe) => {
      // Remove 'active' class from all buttons
      daily.classList.remove('active');
      weekly.classList.remove('active');
      monthly.classList.remove('active');
    
      // Add 'active' class to the clicked button
      $(`.${timeframe}`).classList.add('active');
    
      // Update data for all activities
      getData(0, workDur, workPrev, timeframe);
      getData(1, playDur, playPrev, timeframe);
      getData(2, studyDur, studyPrev, timeframe);
      getData(3, exerciseDur, exercisePrev, timeframe);
      getData(4, socialDur, socialPrev, timeframe);
      getData(5, selfcareDur, selfcarePrev, timeframe);
    
      // Update the "Last [timeframe]" text
      const timeframeText = {
        daily: 'Previous Day - ',
        weekly: 'Last Week - ',
        monthly: 'Last Month - ',
      };
      work.textContent = timeframeText[timeframe];
      play.textContent = timeframeText[timeframe];
      study.textContent = timeframeText[timeframe];
      exercise.textContent = timeframeText[timeframe];
      social.textContent = timeframeText[timeframe];
      selfcare.textContent = timeframeText[timeframe];
    };
    
    // Event listeners
    daily.addEventListener('click', () => setTimeframe('daily'));
    weekly.addEventListener('click', () => setTimeframe('weekly'));
    monthly.addEventListener('click', () => setTimeframe('monthly'));
    
    Marked as helpful
  • Piotr•260
    @Gwynbleidd222
    Submitted 4 months ago

    This is my solution to the newsletter-sign-up on Frontend Mentor

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 4 months ago

    Great work here!

    Some improvement would be to grab the email provided and display it in the success message and add comments.

    const emailSpan = document.querySelector('.description-two span'); // Span to display the submitted email
    
    // Function to handle form submission and validation
    const checkInput = () => {
      const emailValue = mail.value.trim(); // Get and trim the email input value
    
      if (isValidEmail(emailValue)) {
        // If email is valid:
        sectionOne.style.opacity = '0'; // Hide the form section
        sectionTwo.classList.add('active'); // Show the success message section
        errorMsg.textContent = ''; // Clear any error message
        emailSpan.textContent = emailValue; // Dynamically update the email in the success message
      } else {
        // If email is invalid:
        errorMsg.textContent = 'Valid email required!'; // Show error message
        mail.classList.add('error-input'); // Add error styling to the input field
      }
    };
    
    
    

    The success message display can also be restyle to match the figma design better.

  • Louay997•90
    @Louay997
    Submitted 4 months ago

    article_preview_component

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 4 months ago

    Great work here. Here are some area for improvements

    • The background-color can be set to #ecf2f8.
    • The copy area can be padded.
    • The info_container3 class can be given a display of flex, align-items of center, a column-gap of 64px and a justify-content of space-between.
    Marked as helpful
  • Guilherme de Oliveira Santa Rosa•240
    @GuilhermeOSR
    Submitted about 2 years ago

    Meet Landing Page Project - Newbie

    1
    P
    Habeeb Kareem•690
    @olaide-hok
    Posted 4 months ago

    I can't preview your site nor the code repository.

Frontend Mentor logo

Stay up to datewith new challenges, featured solutions, selected articles, and our latest news

Frontend Mentor

  • Unlock Pro
  • Contact us
  • FAQs
  • Become a partner

Explore

  • Learning paths
  • Challenges
  • Solutions
  • Articles

Community

  • Discord
  • Guidelines

For companies

  • Hire developers
  • Train developers
© Frontend Mentor 2019 - 2025
  • Terms
  • Cookie Policy
  • Privacy Policy
  • License

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub

Oops! 😬

You need to be logged in before you can do that.

Log in with GitHub