diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..1c32db2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,172 @@ +name: Release Pipeline + +on: + workflow_dispatch: + inputs: + mode: + description: "Choose whether to create a new tagged release or deploy an existing tag" + required: true + type: choice + default: release + options: + - release + - deploy-existing-tag + version: + description: "Version without the v prefix (examples: 1.2.0, 1.2.0-rc.1)" + required: true + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: release-pages + cancel-in-progress: false + +jobs: + validate: + runs-on: ubuntu-latest + outputs: + tag_name: ${{ steps.meta.outputs.tag_name }} + source_ref: ${{ steps.meta.outputs.source_ref }} + is_prerelease: ${{ steps.meta.outputs.is_prerelease }} + should_create_release: ${{ steps.meta.outputs.should_create_release }} + steps: + - name: Validate version format + env: + VERSION: ${{ inputs.version }} + run: | + python - <<'PY' + import os, re, sys + version = os.environ["VERSION"] + pattern = r"\d+\.\d+\.\d+(?:-(?:alpha|beta|rc)\.\d+)?" + if not re.fullmatch(pattern, version): + print(f"Invalid version: {version}") + print("Expected formats: 1.2.0, 1.2.0-alpha.1, 1.2.0-beta.1, 1.2.0-rc.1") + sys.exit(1) + PY + + - name: Restrict new releases to main + if: inputs.mode == 'release' + run: | + if [[ "${{ github.ref_name }}" != "main" ]]; then + echo "New releases must be dispatched from the main branch." + exit 1 + fi + + - name: Checkout repository metadata + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Resolve release metadata + id: meta + env: + MODE: ${{ inputs.mode }} + VERSION: ${{ inputs.version }} + DISPATCH_REF: ${{ github.ref_name }} + run: | + TAG_NAME="v${VERSION}" + + if [[ "$MODE" == "release" ]]; then + if git ls-remote --exit-code --tags origin "refs/tags/${TAG_NAME}" >/dev/null 2>&1; then + echo "Tag ${TAG_NAME} already exists on origin." + exit 1 + fi + SOURCE_REF="${GITHUB_SHA}" + SHOULD_CREATE_RELEASE=true + else + if ! git ls-remote --exit-code --tags origin "refs/tags/${TAG_NAME}" >/dev/null 2>&1; then + echo "Tag ${TAG_NAME} does not exist on origin." + exit 1 + fi + SOURCE_REF="${TAG_NAME}" + SHOULD_CREATE_RELEASE=false + fi + + if [[ "$VERSION" == *-* ]]; then + IS_PRERELEASE=true + else + IS_PRERELEASE=false + fi + + { + echo "tag_name=${TAG_NAME}" + echo "source_ref=${SOURCE_REF}" + echo "is_prerelease=${IS_PRERELEASE}" + echo "should_create_release=${SHOULD_CREATE_RELEASE}" + } >> "$GITHUB_OUTPUT" + + build: + runs-on: ubuntu-latest + needs: validate + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.validate.outputs.source_ref }} + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Set Eleventy path prefix + run: | + REPO_NAME="${GITHUB_REPOSITORY#*/}" + if [[ "$REPO_NAME" == *.github.io ]]; then + echo "ELEVENTY_PATH_PREFIX=/" >> "$GITHUB_ENV" + else + echo "ELEVENTY_PATH_PREFIX=/${REPO_NAME}/" >> "$GITHUB_ENV" + fi + + - run: npm ci + - run: npm run build:css + - run: npx eleventy --pathprefix="$ELEVENTY_PATH_PREFIX" + - run: npm run check + + - uses: actions/upload-pages-artifact@v3 + with: + path: _site + + release: + runs-on: ubuntu-latest + needs: [validate, build] + if: needs.validate.outputs.should_create_release == 'true' + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Create and push tag + env: + TAG_NAME: ${{ needs.validate.outputs.tag_name }} + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git fetch --tags origin + git tag "$TAG_NAME" "$GITHUB_SHA" + git push origin "$TAG_NAME" + + - name: Create GitHub release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ needs.validate.outputs.tag_name }} + generate_release_notes: true + prerelease: ${{ needs.validate.outputs.is_prerelease == 'true' }} + + deploy: + runs-on: ubuntu-latest + needs: [validate, build, release] + if: | + always() && + needs.build.result == 'success' && + (needs.release.result == 'success' || needs.release.result == 'skipped') + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - id: deployment + uses: actions/deploy-pages@v4 diff --git a/_includes/footer.njk b/_includes/footer.njk index 3965a16..9a5091d 100644 --- a/_includes/footer.njk +++ b/_includes/footer.njk @@ -16,7 +16,7 @@ {% set projectLabel = "Project" if not isUzPage else "Loyiha" %} {% set homeLabel = "Home" if not isUzPage else "Bosh sahifa" %} {% set tutorialLabel = "Tutorials" if not isUzPage else "Darslar" %} -{% set toolsLabel = "Online IDE" if not isUzPage else "Onlayn IDE" %} +{% set toolsLabel = "Python Lab" %} {% set joinGithubLabel = "Join on GitHub" if not isUzPage else "GitHub'da qo'shiling" %} {% set contributeLabel = "Contributing Guide" if not isUzPage else "Hissa qo'shish qo'llanmasi" %} {% set issueLabel = "Report an Issue" if not isUzPage else "Muammo haqida yozing" %} diff --git a/_includes/head.njk b/_includes/head.njk index 0c4d7b1..c851dae 100644 --- a/_includes/head.njk +++ b/_includes/head.njk @@ -5,7 +5,7 @@ {% set pageDescription = description or defaultSiteDescription %} {% set metaKeywords = site.keywordsUz if (langCode == 'uz' and site.keywordsUz) else site.keywords %} {% if isHomepage %} - {% set pageTitle = ("Python dasturlash darslari | " + site.name) if langCode == 'uz' else ("Python Programming Tutorial | " + site.name) %} + {% set pageTitle = ("Pythonni bepul o'rganing: Boshlang'ichdan advancedgacha darslar | " + site.name) if langCode == 'uz' else ("Learn Python Free: Beginner to Advanced Lessons | " + site.name) %} {% elif isTutorial %} {% set pageTitle = (tutorialDisplayTitle + " — Boshlang'ichdan advanced darajagacha to'liq dars") if langCode == 'uz' else (tutorialDisplayTitle + " - Complete Tutorial Basic to Advanced") %} {% elif title %} @@ -33,7 +33,7 @@ - + @@ -43,7 +43,7 @@ - + @@ -74,14 +74,14 @@ {"@context":"https://schema.org","@type":"WebSite","@id":"{{ site.url }}/#website","url":"{{ site.url }}","name":"{{ site.name }}","description":"{{ pageDescription }}","inLanguage":"{{ 'uz-UZ' if langCode == 'uz' else 'en-US' }}"} {% else %} {% endif %} diff --git a/_includes/landing.njk b/_includes/landing.njk index 6707b21..60af34f 100644 --- a/_includes/landing.njk +++ b/_includes/landing.njk @@ -1,250 +1,364 @@ -
- {% set currentUrl = page.url or '/' %} - {% set isUzPage = (lang or '') == 'uz' or currentUrl == '/uz/' or currentUrl.startsWith('/uz/') %} - {% set contentListTitle = "Python darslari ro'yxati" if isUzPage else "Python Tutorial Content List" %} - {% set contentListSubtitle = "Pythonni bosqichma-bosqich, boshlang'ichdan advanced darajagacha o'rganing. Har bir mavzu bo'yicha aniq qo'llanmalar bilan." if isUzPage else "Learn Python structurally from basics to advanced topics with step-by-step guides." %} - {# Keep level names as-is (Basic/Intermediate/Advanced) #} - {% set basicLabel = "Basic Level" %} - {% set intermediateLabel = "Intermediate Level" %} - {% set advancedLabel = "Advanced Level" %} - {% set basicAlt = "Basic Level" %} - {% set intermediateAlt = "Intermediate Level" %} - {% set advancedAlt = "Advanced Level" %} - {% set basicDesc = "Pythonni noldan boshlang! O'zgaruvchilar, ma'lumot turlari, operatorlar va boshqaruv konstruksiyalarini o'rganasiz. Dasturlashga endi kirayotganlar uchun mos." if isUzPage else "Start your Python adventure from the basics! Learn fundamental concepts like variables, data types, operators, and control structures. Suitable for beginners new to programming." %} - {% set intermediateDesc = "Python ko'nikmalaringizni rivojlantiring. Modullar, fayl bilan ishlash (file I/O), exception'lar va Obyektga yo'naltirilgan dasturlash (OOP) kabi mavzularni o'zlashtirasiz. Asoslarni bilganlar uchun." if isUzPage else "Develop your Python skills further. Master concepts like modules, file I/O, exceptions, and Object-Oriented Programming (OOP). Aimed at those who understand Python basics and want to expand their capabilities." %} - {% set advancedDesc = "Pythonning advanced usullariga chuqurroq kiring. Type hint'lar, decorator'lar, iterator/generator'lar va multithreading hamda async/await kabi professional texnikalarni o'rganasiz." if isUzPage else "Dive deep into advanced Python techniques. Learn type hints, decorators, iterators, generators, and professional programming techniques like multithreading and async/await." %} - {% set defaultTutorialList = collections.tutorialsUz if (isUzPage and collections.tutorialsUz) else collections.tutorials %} - {% set tutorialList = tutorialItems or defaultTutorialList %} - {% set dataStructureItems = collections.dataStructuresUz if isUzPage else collections.dataStructures %} - {% set algorithmItems = collections.algorithmsUz if isUzPage else collections.algorithms %} - {% set dsaSectionTitle = "Ma'lumotlar tuzilmalari va algoritmlar" if isUzPage else "Data Structures & Algorithms" %} - {% set dsaSectionSubtitle = "Samarali dasturiy ta'minotning asoslarini o'rganing. Python kodlari, murakkablik tahlili va amaliy patternlar bilan bosqichma-bosqich yo'l." if isUzPage else "Master the building blocks of efficient software. Implementation-first approach with Python code, complexity analysis, and common patterns." %} - {% set dataStructuresTitle = "Ma'lumotlar tuzilmalari" if isUzPage else "Data Structures" %} - {% set algorithmsTitle = "Algoritmlar" if isUzPage else "Algorithms" %} - {% set dataStructuresRoadmapLabel = "Yo'l xaritasini ochish" if isUzPage else "View roadmap" %} - {% set algorithmsRoadmapLabel = "Yo'l xaritasini ochish" if isUzPage else "View roadmap" %} - {% set dataStructuresRoadmapUrl = '/uz/data-structures/' if isUzPage else '/en/data-structures/' %} - {% set algorithmsRoadmapUrl = '/uz/algorithms/' if isUzPage else '/en/algorithms/' %} - {% set dataStructuresCardCopy = "Necaise kitobi asosidagi yo'nalish: ADT, massivlar, bog'langan tuzilmalar, stack/queue, hash table, daraxtlar, heap va graph mavzulari." if isUzPage else "Book-aligned track (Necaise): ADTs, arrays, linked structures, queues/stacks, hash tables, and trees, plus heap and graph extensions." %} - {% set algorithmsCardCopy = "Necaise kitobi asosidagi yo'nalish: algoritm tahlili, qidirish/saralash, rekursiya va murakkab saralash usullari hamda qo'shimcha graph va dynamic programming mavzulari." if isUzPage else "Book-aligned track (Necaise): algorithm analysis, searching/sorting, recursion, and advanced sorting, plus graph and dynamic programming extensions." %} -
-
-

{{ contentListTitle }}

-

- {{ contentListSubtitle }} -

+{% set currentUrl = page.url or '/' %} +{% set isUzPage = (lang or '') == 'uz' or currentUrl == '/uz/' or currentUrl.startsWith('/uz/') %} +{% set defaultTutorialList = collections.tutorialsUz if (isUzPage and collections.tutorialsUz) else collections.tutorials %} +{% set tutorialList = tutorialItems or defaultTutorialList %} +{% set dataStructureItems = collections.dataStructuresUz if isUzPage else collections.dataStructures %} +{% set algorithmItems = collections.algorithmsUz if isUzPage else collections.algorithms %} +{% set tutorialUrl = '/uz/tutorial/what-is-python/' if isUzPage else '/en/tutorial/what-is-python/' %} +{% set intermediateStartUrl = '/uz/tutorial/python-modules/' if isUzPage else '/en/tutorial/python-modules/' %} +{% set advancedStartUrl = '/uz/tutorial/python-type-hints/' if isUzPage else '/en/tutorial/python-type-hints/' %} +{% set ideUrl = '/online-python-ide/' %} +{% set dataStructuresRoadmapUrl = '/uz/data-structures/' if isUzPage else '/en/data-structures/' %} +{% set algorithmsRoadmapUrl = '/uz/algorithms/' if isUzPage else '/en/algorithms/' %} +{% set totalTutorials = tutorialList.length %} +{% set heroEyebrow = "Bepul Python o'quv yo'li" if isUzPage else "Free Python learning path" %} +{% set heroTitle = "Birinchi dasturdan amaliy ko'nikmagacha Pythonni bosqichma-bosqich o'rganing" if isUzPage else "Learn Python step by step from your first script to practical, advanced topics" %} +{% set heroBody = "3 daraja bo'yicha tuzilgan darslar, brauzer ichidagi IDE va DSA yo'l xaritalari bilan o'rganishni tez boshlang. O'zbek va ingliz tillarida bir xil asosiy yo'nalishlar mavjud." if isUzPage else "Start with a structured curriculum across three levels, practice in a browser IDE, and move into DSA roadmaps when you are ready. The core learning path is available in both English and Uzbek." %} +{% set heroPrimaryLabel = "Darsni boshlash" if isUzPage else "Start lesson 1" %} +{% set heroSecondaryLabel = "Yo'nalishlarni ko'rish" if isUzPage else "Explore learning paths" %} +{% set heroTertiaryLabel = "Python Labni ochish" if isUzPage else "Open Python Lab" %} +{% set heroStatLessons = "ta dars" if isUzPage else "lessons" %} +{% set heroStatLanguages = "2 til" if isUzPage else "2 languages" %} +{% set heroStatTracks = "3 daraja" if isUzPage else "3 levels" %} +{% set heroStatTools = "DSA + IDE" %} +{% set heroGuideTitle = "Qayerdan boshlash kerak?" if isUzPage else "Where should you start?" %} +{% set heroGuideBody = "Agar Python yangi bo'lsa, 1-darsdan boshlang. Agar siz allaqachon o'zgaruvchilar, shartlar va funksiyalarni bilsangiz, o'rta darajaga o'ting. Ishlayotgan kodni tezda sinash kerak bo'lsa, Python Lab'dan foydalaning." if isUzPage else "If Python is new to you, start from lesson 1. If you already know variables, conditions, and functions, jump into intermediate topics. If you want to test code quickly, use the Python Lab." %} +{% set pathsTitle = "O'zingizga mos yo'nalishni tanlang" if isUzPage else "Choose the path that matches your level" %} +{% set pathsBody = "Har bir yo'nalish boshqa holat uchun mo'ljallangan: boshlash, bilimni chuqurlashtirish yoki professional uslublarga o'tish." if isUzPage else "Each track is built for a different moment: getting started, building working fluency, or moving into more professional Python patterns." %} +{% set basicLabel = "Boshlang'ich" if isUzPage else "Beginner" %} +{% set intermediateLabel = "O'rta" if isUzPage else "Intermediate" %} +{% set advancedLabel = "Advanced" %} +{% set basicSummary = "Agar Python siz uchun yangi bo'lsa, shu yerdan boshlang. Sintaksis, o'zgaruvchilar, shartlar, sikllar va funksiyalar bilan mustahkam poydevor yarating." if isUzPage else "If Python is new to you, start here. Build a solid foundation with syntax, variables, conditions, loops, and functions." %} +{% set intermediateSummary = "Agar asoslarni bilsangiz, shu yo'nalishga o'ting. Modullar, fayllar, exception'lar, OOP va amaliy Python ish jarayonlarini o'rganing." if isUzPage else "If you already know the basics, move into this track. Learn modules, files, exceptions, OOP, and practical Python workflows." %} +{% set advancedSummary = "Agar siz allaqachon ishlaydigan Python yozsangiz, shu bo'limdan chuqurlashing. Type hints, decorators, async, testing va design pattern'larni o'rganing." if isUzPage else "If you already write working Python, go deeper here. Study type hints, decorators, async, testing, and design patterns." %} +{% set basicTopics = "O'zgaruvchilar, ma'lumot turlari, operatorlar, shartlar, sikllar, funksiyalar" if isUzPage else "Variables, data types, operators, conditions, loops, functions" %} +{% set intermediateTopics = "Modullar, file I/O, exception handling, class/object, virtual environment" if isUzPage else "Modules, file I/O, exception handling, classes/objects, virtual environments" %} +{% set advancedTopics = "Type hints, iterators, context managers, async/await, pytest, FastAPI" if isUzPage else "Type hints, iterators, context managers, async/await, pytest, FastAPI" %} +{% set basicCta = "Boshlang'ichni ochish" if isUzPage else "Open beginner track" %} +{% set intermediateCta = "O'rta darajani ochish" if isUzPage else "Open intermediate track" %} +{% set advancedCta = "Advanced darslarni ochish" if isUzPage else "Open advanced track" %} +{% set curriculumTitle = "To'liq o'quv dasturi" if isUzPage else "Full curriculum" %} +{% set curriculumBody = "Agar sizga to'liq indeks kerak bo'lsa, barcha darslar quyida darajalar bo'yicha joylashtirilgan. Havolalar va URL'lar o'zgarmagan." if isUzPage else "If you prefer a complete index, all lessons are grouped below by level. Links and URLs remain unchanged." %} +{% set curriculumHintTitle = "Tez tavsiya" if isUzPage else "Quick recommendation" %} +{% set curriculumHintBody = "Ko'pchilik uchun eng to'g'ri boshlanish nuqtasi birinchi darsdir. Agar siz sintaksisni allaqachon bilsangiz, to'g'ridan-to'g'ri modullar va file I/O bo'limiga o'ting." if isUzPage else "For most people, the right starting point is lesson 1. If you already know the syntax basics, jump straight to modules and file I/O." %} +{% set basicSectionTitle = "Boshlang'ich darslar" if isUzPage else "Beginner lessons" %} +{% set intermediateSectionTitle = "O'rta darajadagi darslar" if isUzPage else "Intermediate lessons" %} +{% set advancedSectionTitle = "Advanced darslar" if isUzPage else "Advanced lessons" %} +{% set basicSectionBody = "Asosiy tushunchalar va kundalik Python yozish uchun kerak bo'ladigan minimum bilim." if isUzPage else "The essential concepts you need to read and write everyday Python." %} +{% set intermediateSectionBody = "Kodni amaliyroq qilish uchun modullar, fayllar va obyektga yo'naltirilgan yondashuvlar." if isUzPage else "Modules, files, and object-oriented concepts that make your code more practical." %} +{% set advancedSectionBody = "Yirikroq yoki murakkabroq loyihalar uchun foydali bo'lgan professional texnikalar." if isUzPage else "Professional techniques that help when your codebase or problem space gets more complex." %} +{% set dsaSectionTitle = "Keyingi qadam: Data Structures & Algorithms" if isUzPage else "Next step: Data Structures & Algorithms" %} +{% set dsaSectionBody = "Bu bo'lim boshlang'ich Python yo'lidan alohida. Undan algoritmik fikrlashni kuchaytirish, intervyu tayyorlov yoki computer science asoslarini mustahkamlash uchun foydalaning." if isUzPage else "This track sits alongside the main Python curriculum. Use it to strengthen algorithmic thinking, prepare for interviews, or build computer science fundamentals." %} +{% set dsaLocalizedNote = "DSA bo'limlari bosqichma-bosqich kengaymoqda; yo'l xaritalari boshlash uchun eng yaxshi nuqta." if isUzPage else "Roadmaps are the best entry point if you want a guided sequence." %} +{% set dataStructuresTitle = "Data Structures" %} +{% set algorithmsTitle = "Algorithms" %} +{% set dataStructuresBody = "ADT, array, linked structure, stack/queue, hash table, tree va graph mavzularini bosqichma-bosqich o'rganing." if isUzPage else "Study ADTs, arrays, linked structures, queues/stacks, hash tables, trees, and graph representations in a gradual sequence." %} +{% set algorithmsBody = "Murakkablik tahlili, qidirish, saralash, graph algoritmlari va dynamic programming mavzulariga o'ting." if isUzPage else "Move from complexity analysis into searching, sorting, graph algorithms, and dynamic programming." %} +{% set roadmapLabel = "Yo'l xaritasini ochish" if isUzPage else "Open roadmap" %} +{% set languageTitle = "Ikki tilda o'rganing" if isUzPage else "Learn in two languages" %} +{% set languageBody = "Asosiy Python kursi ingliz va o'zbek tillarida mavjud. Bir xil yo'l bilan ishlang va kerak bo'lsa tilni almashtiring." if isUzPage else "The core Python curriculum is available in both English and Uzbek. Follow the same structure and switch languages whenever it helps your learning." %} +{% set languagePrimary = "O'zbek sahifasiga o'tish" if not isUzPage else "English homepagega o'tish" %} +{% set languageUrl = '/uz/' if not isUzPage else '/en/' %} + +
+
+ - -
-
-
- - - -
-
+
+
+ +
+
+
+

{{ pathsTitle }}

+

{{ pathsBody }}

+
+ +
+
+
+
+ {{ basicLabel }} +
+

{{ basicLabel }}

+
+
+

{{ basicSummary }}

+
+

{{ "Asosiy mavzular" if isUzPage else "Key topics" }}

+

{{ basicTopics }}

+
+
+ +
+ +
+
+
+ {{ intermediateLabel }} +
+

{{ intermediateLabel }}

+
+
+

{{ intermediateSummary }}

+
+

{{ "Asosiy mavzular" if isUzPage else "Key topics" }}

+

{{ intermediateTopics }}

+
+
+ +
+ +
+
+
+ {{ advancedLabel }} +
+

{{ advancedLabel }}

+
+
+

{{ advancedSummary }}

+
+

{{ "Asosiy mavzular" if isUzPage else "Key topics" }}

+

{{ advancedTopics }}

+
+
+ +
+
+
+
+ +
+
+
+
+

{{ curriculumTitle }}

+

{{ curriculumBody }}

+
+ +
+ +
+
+
+
+ {{ basicLabel }} +
+

{{ basicSectionTitle }}

+

{{ basicSectionBody }}

- -
-
-
-
-
- {{ basicAlt }} -
-
-

{{ basicLabel }}

-
-
-
- -

- {{ basicDesc }} -

-
-
-
-
-
- {% for tutorial in tutorialList %} - {% set tutorialLevel = "basic" %} - {% if tutorial.data.order > 32 %} - {% set tutorialLevel = "advanced" %} - {% elif tutorial.data.order > 21 %} - {% set tutorialLevel = "intermediate" %} - {% endif %} - - {{ tutorial.data.order | padStart }}. - {{ tutorial.data.title | displayTutorialTitle }} - - {% endfor %} -
+
+
+
+ {% for tutorial in tutorialList %} + {% if tutorial.data.order <= 21 %} + + {{ tutorial.data.order | padStart }}. + {{ tutorial.data.title | displayTutorialTitle }} + + {% endif %} + {% endfor %} +
+
+ +
+
+
+ {{ intermediateLabel }} +
+

{{ intermediateSectionTitle }}

+

{{ intermediateSectionBody }}

-
- -
-
-
-

{{ dsaSectionTitle }}

-

- {{ dsaSectionSubtitle }} -

-
-
- -
-
- {{ dataStructuresTitle }} -

{{ dataStructuresTitle }}

-
-

{{ dataStructuresCardCopy }}

-
- {% for item in dataStructureItems %} - {% if item.data.order > 0 %} - - {{ item.data.order | padStart }}. - {{ item.data.title }} - - {% endif %} - {% endfor %} -
- - {{ dataStructuresRoadmapLabel }} - - - - -
- -
-
- {{ algorithmsTitle }} -

{{ algorithmsTitle }}

-
-

{{ algorithmsCardCopy }}

-
- {% for item in algorithmItems %} - {% if item.data.order > 0 %} - - {{ item.data.order | padStart }}. - {{ item.data.title }} - - {% endif %} - {% endfor %} -
- - {{ algorithmsRoadmapLabel }} - - - - -
-
+
+
+
+ {% for tutorial in tutorialList %} + {% if tutorial.data.order > 21 and tutorial.data.order <= 32 %} + + {{ tutorial.data.order | padStart }}. + {{ tutorial.data.title | displayTutorialTitle }} + + {% endif %} + {% endfor %} +
+
+ +
+
+
+ {{ advancedLabel }} +
+

{{ advancedSectionTitle }}

+

{{ advancedSectionBody }}

-
- - +
+
+
+ {% for tutorial in tutorialList %} + {% if tutorial.data.order > 32 %} + + {{ tutorial.data.order | padStart }}. + {{ tutorial.data.title | displayTutorialTitle }} + + {% endif %} + {% endfor %} +
+
+ + + + +
+
+
+
+

{{ "Bilingual" if not isUzPage else "Ikki til" }}

+

{{ languageTitle }}

+

{{ languageBody }}

+ + {{ languagePrimary }} + +
+
+
+

{{ "Primary path" if not isUzPage else "Asosiy yo'nalish" }}

+

{{ "Begin with the core Python lessons, then branch into DSA or tools when you need practice and depth." if not isUzPage else "Avval asosiy Python darslarini tugating, so'ng amaliyot va chuqurlik uchun DSA yoki vositalarga o'ting." }}

+
+
+

{{ "Open source" if not isUzPage else "Open source" }}

+

{{ "The curriculum stays easy to review and improve because the project remains linkable, static, and GitHub-friendly." if not isUzPage else "Loyiha statik va GitHub bilan qulay ishlashi sababli o'quv dasturini ko'rib chiqish va yaxshilash oson qoladi." }}

+
+
+
+
+
+ +
+
+
+

{{ dsaSectionTitle }}

+

{{ dsaSectionBody }}

+

{{ dsaLocalizedNote }}

+
+ +
+ + + +
+
+
diff --git a/_includes/navbar.njk b/_includes/navbar.njk index ffb6abb..bd97437 100644 --- a/_includes/navbar.njk +++ b/_includes/navbar.njk @@ -11,7 +11,8 @@ {% set dsaUrl = '/uz/data-structures/' if isUzPage else '/en/data-structures/' %} {% set tutorialLabel = "Darslar" if isUzPage else "Tutorial" %} {% set githubLabel = "GitHub'da qo'shiling" if isUzPage else "Join on Github" %} -{% set ideLabel = "Onlayn IDE" if isUzPage else "Online IDE" %} +{% set ideLabel = "Python Lab" %} +{% set ideShortLabel = "Lab" %} {% set languageSwitchUrl = '/uz/' if not isUzPage else '/en/' %} {% if currentUrl == '/' %} {% set languageSwitchUrl = '/uz/' %} @@ -79,7 +80,7 @@ {% endif %} - logo-belajarpython + Python Tutorial logo {% if not isToolPage %} @@ -95,8 +96,8 @@ href="{{ '/online-python-ide/' | url }}" target="_blank" rel="noopener"> - - IDE + + {{ ideShortLabel }} - + {{ ideLabel }} :not([hidden]) ~ :not([hidden]) { - --tw-space-y-reverse: 0; - margin-top: calc(0.25rem * calc(1 - var(--tw-space-y-reverse))); - margin-bottom: calc(0.25rem * var(--tw-space-y-reverse)); -} - .space-y-2 > :not([hidden]) ~ :not([hidden]) { --tw-space-y-reverse: 0; margin-top: calc(0.5rem * calc(1 - var(--tw-space-y-reverse))); margin-bottom: calc(0.5rem * var(--tw-space-y-reverse)); } +.space-y-3 > :not([hidden]) ~ :not([hidden]) { + --tw-space-y-reverse: 0; + margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse))); + margin-bottom: calc(0.75rem * var(--tw-space-y-reverse)); +} + .self-start { align-self: flex-start; } @@ -1148,10 +1161,6 @@ video { overflow: hidden; } -.overflow-x-auto { - overflow-x: auto; -} - .overflow-y-auto { overflow-y: auto; } @@ -1184,6 +1193,18 @@ video { border-radius: 1rem; } +.rounded-\[1\.5rem\] { + border-radius: 1.5rem; +} + +.rounded-\[1\.75rem\] { + border-radius: 1.75rem; +} + +.rounded-\[2rem\] { + border-radius: 2rem; +} + .rounded-full { border-radius: 9999px; } @@ -1213,10 +1234,6 @@ video { border-width: 1px; } -.border-2 { - border-width: 2px; -} - .border-x { border-left-width: 1px; border-right-width: 1px; @@ -1251,11 +1268,21 @@ video { border-color: rgb(51 65 85 / var(--tw-border-opacity, 1)); } +.border-amber-200 { + --tw-border-opacity: 1; + border-color: rgb(253 230 138 / var(--tw-border-opacity, 1)); +} + .border-blue-500 { --tw-border-opacity: 1; border-color: rgb(59 130 246 / var(--tw-border-opacity, 1)); } +.border-primary-100 { + --tw-border-opacity: 1; + border-color: rgb(224 242 254 / var(--tw-border-opacity, 1)); +} + .border-primary-500 { --tw-border-opacity: 1; border-color: rgb(42 88 128 / var(--tw-border-opacity, 1)); @@ -1266,11 +1293,6 @@ video { border-color: rgb(29 63 91 / var(--tw-border-opacity, 1)); } -.border-primary-900 { - --tw-border-opacity: 1; - border-color: rgb(30 41 51 / var(--tw-border-opacity, 1)); -} - .border-slate-100 { --tw-border-opacity: 1; border-color: rgb(241 245 249 / var(--tw-border-opacity, 1)); @@ -1305,6 +1327,10 @@ video { border-color: transparent; } +.border-white\/15 { + border-color: rgb(255 255 255 / 0.15); +} + .border-white\/20 { border-color: rgb(255 255 255 / 0.2); } @@ -1313,6 +1339,11 @@ video { border-color: rgb(255 255 255 / 0.3); } +.border-yellow-200 { + --tw-border-opacity: 1; + border-color: rgb(254 240 138 / var(--tw-border-opacity, 1)); +} + .bg-\[\#0a0f1a\] { --tw-bg-opacity: 1; background-color: rgb(10 15 26 / var(--tw-bg-opacity, 1)); @@ -1338,6 +1369,11 @@ video { background-color: rgb(60 59 110 / var(--tw-bg-opacity, 1)); } +.bg-amber-50 { + --tw-bg-opacity: 1; + background-color: rgb(255 251 235 / var(--tw-bg-opacity, 1)); +} + .bg-black\/50 { background-color: rgb(0 0 0 / 0.5); } @@ -1372,14 +1408,14 @@ video { background-color: rgb(28 58 85 / var(--tw-bg-opacity, 1)); } -.bg-slate-100 { +.bg-secondary-400 { --tw-bg-opacity: 1; - background-color: rgb(241 245 249 / var(--tw-bg-opacity, 1)); + background-color: rgb(255 223 118 / var(--tw-bg-opacity, 1)); } -.bg-slate-200 { +.bg-slate-100 { --tw-bg-opacity: 1; - background-color: rgb(226 232 240 / var(--tw-bg-opacity, 1)); + background-color: rgb(241 245 249 / var(--tw-bg-opacity, 1)); } .bg-slate-50 { @@ -1411,6 +1447,10 @@ video { background-color: rgb(2 6 23 / var(--tw-bg-opacity, 1)); } +.bg-slate-950\/20 { + background-color: rgb(2 6 23 / 0.2); +} + .bg-transparent { background-color: transparent; } @@ -1428,11 +1468,20 @@ video { background-color: rgb(255 255 255 / 0.2); } +.bg-white\/80 { + background-color: rgb(255 255 255 / 0.8); +} + .bg-yellow-400 { --tw-bg-opacity: 1; background-color: rgb(250 204 21 / var(--tw-bg-opacity, 1)); } +.bg-yellow-50 { + --tw-bg-opacity: 1; + background-color: rgb(254 252 232 / var(--tw-bg-opacity, 1)); +} + .bg-opacity-20 { --tw-bg-opacity: 0.2; } @@ -1441,6 +1490,10 @@ video { background-image: linear-gradient(to bottom, var(--tw-gradient-stops)); } +.bg-gradient-to-br { + background-image: linear-gradient(to bottom right, var(--tw-gradient-stops)); +} + .bg-gradient-to-l { background-image: linear-gradient(to left, var(--tw-gradient-stops)); } @@ -1455,6 +1508,12 @@ video { --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); } +.from-primary-900 { + --tw-gradient-from: #1e2933 var(--tw-gradient-from-position); + --tw-gradient-to: rgb(30 41 51 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} + .from-secondary-500 { --tw-gradient-from: #fdd03f var(--tw-gradient-from-position); --tw-gradient-to: rgb(253 208 63 / 0) var(--tw-gradient-to-position); @@ -1467,6 +1526,15 @@ video { --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); } +.via-primary-700 { + --tw-gradient-to: rgb(29 63 91 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), #1d3f5b var(--tw-gradient-via-position), var(--tw-gradient-to); +} + +.to-primary-500 { + --tw-gradient-to: #2a5880 var(--tw-gradient-to-position); +} + .to-primary-800 { --tw-gradient-to: #1c3a55 var(--tw-gradient-to-position); } @@ -1487,6 +1555,10 @@ video { padding: 1rem; } +.p-5 { + padding: 1.25rem; +} + .p-6 { padding: 1.5rem; } @@ -1516,6 +1588,11 @@ video { padding-right: 1.25rem; } +.px-6 { + padding-left: 1.5rem; + padding-right: 1.5rem; +} + .py-0\.5 { padding-top: 0.125rem; padding-bottom: 0.125rem; @@ -1556,16 +1633,21 @@ video { padding-bottom: 0.75rem; } +.py-4 { + padding-top: 1rem; + padding-bottom: 1rem; +} + +.py-5 { + padding-top: 1.25rem; + padding-bottom: 1.25rem; +} + .py-6 { padding-top: 1.5rem; padding-bottom: 1.5rem; } -.py-8 { - padding-top: 2rem; - padding-bottom: 2rem; -} - .py-\[6px\] { padding-top: 6px; padding-bottom: 6px; @@ -1595,10 +1677,6 @@ video { padding-right: 1rem; } -.pr-9 { - padding-right: 2.25rem; -} - .pt-1 { padding-top: 0.25rem; } @@ -1611,8 +1689,8 @@ video { padding-top: 1rem; } -.text-left { - text-align: left; +.pt-6 { + padding-top: 1.5rem; } .text-center { @@ -1637,6 +1715,11 @@ video { line-height: 2.25rem; } +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} + .text-\[10px\] { font-size: 10px; } @@ -1645,10 +1728,6 @@ video { font-size: 11px; } -.text-\[15px\] { - font-size: 15px; -} - .text-\[20px\] { font-size: 20px; } @@ -1702,6 +1781,10 @@ video { line-height: 1.5rem; } +.leading-7 { + line-height: 1.75rem; +} + .leading-none { line-height: 1; } @@ -1714,6 +1797,22 @@ video { line-height: 1.625; } +.leading-tight { + line-height: 1.25; +} + +.tracking-\[0\.18em\] { + letter-spacing: 0.18em; +} + +.tracking-\[0\.22em\] { + letter-spacing: 0.22em; +} + +.tracking-\[0\.2em\] { + letter-spacing: 0.2em; +} + .tracking-wide { letter-spacing: 0.025em; } @@ -1736,11 +1835,6 @@ video { color: rgb(37 211 102 / var(--tw-text-opacity, 1)); } -.text-\[\#29618a\] { - --tw-text-opacity: 1; - color: rgb(41 97 138 / var(--tw-text-opacity, 1)); -} - .text-\[\#306b96\] { --tw-text-opacity: 1; color: rgb(48 107 150 / var(--tw-text-opacity, 1)); @@ -1751,6 +1845,11 @@ video { color: rgb(226 232 240 / var(--tw-text-opacity, 1)); } +.text-amber-700 { + --tw-text-opacity: 1; + color: rgb(180 83 9 / var(--tw-text-opacity, 1)); +} + .text-black { --tw-text-opacity: 1; color: rgb(0 0 0 / var(--tw-text-opacity, 1)); @@ -1796,11 +1895,36 @@ video { color: rgb(42 88 128 / var(--tw-text-opacity, 1)); } +.text-primary-600 { + --tw-text-opacity: 1; + color: rgb(35 76 110 / var(--tw-text-opacity, 1)); +} + +.text-primary-700 { + --tw-text-opacity: 1; + color: rgb(29 63 91 / var(--tw-text-opacity, 1)); +} + +.text-primary-800 { + --tw-text-opacity: 1; + color: rgb(28 58 85 / var(--tw-text-opacity, 1)); +} + +.text-primary-900 { + --tw-text-opacity: 1; + color: rgb(30 41 51 / var(--tw-text-opacity, 1)); +} + .text-red-400 { --tw-text-opacity: 1; color: rgb(248 113 113 / var(--tw-text-opacity, 1)); } +.text-secondary-400 { + --tw-text-opacity: 1; + color: rgb(255 223 118 / var(--tw-text-opacity, 1)); +} + .text-slate-200 { --tw-text-opacity: 1; color: rgb(226 232 240 / var(--tw-text-opacity, 1)); @@ -1855,6 +1979,11 @@ video { color: rgb(250 204 21 / var(--tw-text-opacity, 1)); } +.text-yellow-700 { + --tw-text-opacity: 1; + color: rgb(161 98 7 / var(--tw-text-opacity, 1)); +} + .no-underline { text-decoration-line: none; } @@ -1921,6 +2050,12 @@ video { outline-style: solid; } +.ring { + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000); +} + .blur { --tw-blur: blur(8px); filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); @@ -1935,6 +2070,12 @@ video { filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); } +.backdrop-blur-sm { + --tw-backdrop-blur: blur(4px); + -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); + backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); +} + .backdrop-filter { -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia); @@ -2226,12 +2367,6 @@ main code { border-color: rgb(59 130 246 / var(--tw-border-opacity, 1)); } -.hover\:scale-\[1\.15\]:hover { - --tw-scale-x: 1.15; - --tw-scale-y: 1.15; - transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y)); -} - .hover\:border-slate-400:hover { --tw-border-opacity: 1; border-color: rgb(148 163 184 / var(--tw-border-opacity, 1)); @@ -2257,9 +2392,22 @@ main code { background-color: rgb(99 102 241 / var(--tw-bg-opacity, 1)); } -.hover\:bg-slate-300:hover { +.hover\:bg-primary-700:hover { --tw-bg-opacity: 1; - background-color: rgb(203 213 225 / var(--tw-bg-opacity, 1)); + background-color: rgb(29 63 91 / var(--tw-bg-opacity, 1)); +} + +.hover\:bg-secondary-500:hover { + --tw-bg-opacity: 1; + background-color: rgb(253 208 63 / var(--tw-bg-opacity, 1)); +} + +.hover\:bg-white\/10:hover { + background-color: rgb(255 255 255 / 0.1); +} + +.hover\:bg-white\/15:hover { + background-color: rgb(255 255 255 / 0.15); } .hover\:bg-white\/20:hover { @@ -2271,11 +2419,6 @@ main code { background-color: rgb(253 224 71 / var(--tw-bg-opacity, 1)); } -.hover\:bg-yellow-50:hover { - --tw-bg-opacity: 1; - background-color: rgb(254 252 232 / var(--tw-bg-opacity, 1)); -} - .hover\:text-black:hover { --tw-text-opacity: 1; color: rgb(0 0 0 / var(--tw-text-opacity, 1)); @@ -2291,11 +2434,6 @@ main code { color: rgb(42 88 128 / var(--tw-text-opacity, 1)); } -.hover\:text-primary-700:hover { - --tw-text-opacity: 1; - color: rgb(29 63 91 / var(--tw-text-opacity, 1)); -} - .hover\:text-secondary-400:hover { --tw-text-opacity: 1; color: rgb(255 223 118 / var(--tw-text-opacity, 1)); @@ -2362,9 +2500,9 @@ main code { background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1)); } -.group:hover .group-hover\:text-\[\#1d5883\] { +.group:hover .group-hover\:text-primary-700 { --tw-text-opacity: 1; - color: rgb(29 88 131 / var(--tw-text-opacity, 1)); + color: rgb(29 63 91 / var(--tw-text-opacity, 1)); } .dark\:border-slate-600:is(.dark *) { @@ -2417,31 +2555,25 @@ main code { color: rgb(255 255 255 / var(--tw-text-opacity, 1)); } -@media (min-width: 768px) { - .md\:mb-12 { - margin-bottom: 3rem; - } - - .md\:mb-16 { - margin-bottom: 4rem; - } - - .md\:mb-4 { - margin-bottom: 1rem; +@media (min-width: 640px) { + .sm\:auto-rows-fr { + grid-auto-rows: minmax(0, 1fr); } - .md\:mr-0 { - margin-right: 0px; + .sm\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); } - .md\:mr-10 { - margin-right: 2.5rem; + .sm\:flex-row { + flex-direction: row; } - .md\:mt-0 { - margin-top: 0px; + .sm\:flex-wrap { + flex-wrap: wrap; } +} +@media (min-width: 768px) { .md\:block { display: block; } @@ -2458,32 +2590,52 @@ main code { display: none; } - .md\:h-12 { - height: 3rem; + .md\:h-10 { + height: 2.5rem; } - .md\:h-20 { - height: 5rem; + .md\:h-12 { + height: 3rem; } .md\:h-5 { height: 1.25rem; } - .md\:h-6 { - height: 1.5rem; + .md\:h-auto { + height: auto; + } + + .md\:min-h-\[3rem\] { + min-height: 3rem; } - .md\:h-9 { - height: 2.25rem; + .md\:min-h-\[5\.5rem\] { + min-height: 5.5rem; } - .md\:h-auto { - height: auto; + .md\:min-h-\[5rem\] { + min-height: 5rem; } - .md\:w-20 { - width: 5rem; + .md\:min-h-\[6\.5rem\] { + min-height: 6.5rem; + } + + .md\:min-h-\[6rem\] { + min-height: 6rem; + } + + .md\:min-h-\[7rem\] { + min-height: 7rem; + } + + .md\:min-h-\[8rem\] { + min-height: 8rem; + } + + .md\:min-h-\[9rem\] { + min-height: 9rem; } .md\:w-3\/12 { @@ -2494,10 +2646,6 @@ main code { width: 1.25rem; } - .md\:w-6 { - width: 1.5rem; - } - .md\:w-9\/12 { width: 75%; } @@ -2510,10 +2658,6 @@ main code { width: 55%; } - .md\:w-auto { - width: auto; - } - .md\:w-full { width: 100%; } @@ -2530,10 +2674,6 @@ main code { flex-direction: row; } - .md\:items-start { - align-items: flex-start; - } - .md\:items-end { align-items: flex-end; } @@ -2542,31 +2682,15 @@ main code { align-items: center; } - .md\:justify-center { - justify-content: center; - } - .md\:justify-between { justify-content: space-between; } - .md\:gap-4 { - gap: 1rem; - } - - .md\:gap-6 { - gap: 1.5rem; - } - .md\:gap-x-4 { -moz-column-gap: 1rem; column-gap: 1rem; } - .md\:overflow-visible { - overflow: visible; - } - .md\:border-t-0 { border-top-width: 0px; } @@ -2575,15 +2699,6 @@ main code { padding: 1.5rem; } - .md\:p-8 { - padding: 2rem; - } - - .md\:px-0 { - padding-left: 0px; - padding-right: 0px; - } - .md\:px-10 { padding-left: 2.5rem; padding-right: 2.5rem; @@ -2604,11 +2719,6 @@ main code { padding-bottom: 3rem; } - .md\:py-14 { - padding-top: 3.5rem; - padding-bottom: 3.5rem; - } - .md\:py-16 { padding-top: 4rem; padding-bottom: 4rem; @@ -2619,23 +2729,6 @@ main code { padding-bottom: 0.75rem; } - .md\:pb-0 { - padding-bottom: 0px; - } - - .md\:pr-20 { - padding-right: 5rem; - } - - .md\:text-center { - text-align: center; - } - - .md\:text-2xl { - font-size: 1.5rem; - line-height: 2rem; - } - .md\:text-3xl { font-size: 1.875rem; line-height: 2.25rem; @@ -2660,9 +2753,52 @@ main code { font-size: 1.125rem; line-height: 1.75rem; } +} - .md\:text-xl { - font-size: 1.25rem; - line-height: 1.75rem; +@media (min-width: 1024px) { + .lg\:min-h-\[9rem\] { + min-height: 9rem; + } + + .lg\:auto-rows-fr { + grid-auto-rows: minmax(0, 1fr); + } + + .lg\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .lg\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .lg\:grid-cols-\[minmax\(0\2c 0\.9fr\)_minmax\(0\2c 1\.1fr\)\] { + grid-template-columns: minmax(0,0.9fr) minmax(0,1.1fr); + } + + .lg\:grid-cols-\[minmax\(0\2c 1\.2fr\)_minmax\(18rem\2c 0\.8fr\)\] { + grid-template-columns: minmax(0,1.2fr) minmax(18rem,0.8fr); + } + + .lg\:grid-cols-\[minmax\(0\2c 1fr\)_minmax\(18rem\2c 0\.8fr\)\] { + grid-template-columns: minmax(0,1fr) minmax(18rem,0.8fr); + } + + .lg\:items-start { + align-items: flex-start; + } + + .lg\:items-stretch { + align-items: stretch; + } +} + +@media (min-width: 1280px) { + .xl\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .xl\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); } } diff --git a/assets/img/logos/site-logo-light.svg b/assets/img/logos/site-logo-light.svg new file mode 100644 index 0000000..2c24231 --- /dev/null +++ b/assets/img/logos/site-logo-light.svg @@ -0,0 +1,26 @@ + + Python Tutorial logo + A custom Python Tutorial wordmark with a blue and gold icon and white text. + + + + + + + + + + + + + + + + + + + + Python + TUTORIAL + + diff --git a/assets/img/logos/site-logo-square.svg b/assets/img/logos/site-logo-square.svg new file mode 100644 index 0000000..d68a205 --- /dev/null +++ b/assets/img/logos/site-logo-square.svg @@ -0,0 +1,32 @@ + + Python Tutorial square logo + A square brand mark for Python Tutorial with a custom blue and gold icon and wordmark. + + + + + + + + + + + + + + + + + + + + + + + + + + Python + TUTORIAL + + diff --git a/content/en/index.md b/content/en/index.md index 7ef464a..7c3208d 100644 --- a/content/en/index.md +++ b/content/en/index.md @@ -2,7 +2,7 @@ layout: home.njk lang: en title: Python Programming Tutorial From Basic to Advanced -description: Learn Python for free with complete tutorials. Python programming guide from basic to advanced for data science, machine learning, AI, and web development. Trusted by 100+ campuses & companies since 2015. +description: Learn Python for free with structured lessons from beginner to advanced, plus data structures, algorithms, and a browser-based Python IDE. permalink: /en/ isHomepage: true --- diff --git a/content/uz/index.md b/content/uz/index.md index 206cfa1..3fd1217 100644 --- a/content/uz/index.md +++ b/content/uz/index.md @@ -2,7 +2,7 @@ layout: home.njk lang: uz title: "Python dasturlash darslari: Boshlang'ichdan Advanced darajagacha" -description: "Pythonni bepul o'rganing: to'liq darslar. Data Science, Machine Learning, AI va veb dasturlash uchun Python qo'llanmasi: boshlang'ichdan advanced darajagacha. 2015 yildan beri 100+ universitet va kompaniya bizga ishonch bildiradi." +description: "Pythonni bepul o'rganing: boshlang'ichdan advanced darajagacha tuzilgan darslar, DSA yo'l xaritalari va brauzer ichidagi Python IDE bilan." permalink: /uz/ isHomepage: true --- diff --git a/docs/prd-homepage-improvement.md b/docs/prd-homepage-improvement.md new file mode 100644 index 0000000..d94b525 --- /dev/null +++ b/docs/prd-homepage-improvement.md @@ -0,0 +1,385 @@ +--- +permalink: false +eleventyExcludeFromCollections: true +--- + +# PRD: Homepage Content and UI/UX Improvement + +## Document Status + +- Status: Draft +- Owner: Product / Design / Frontend +- Target surface: Homepage only +- Pages in scope: `/en/`, `/uz/` +- Redirect note: `/` must continue redirecting to `/en/` + +## Assumption + +This PRD assumes "this page" refers to the bilingual homepage experience rendered through [`_includes/landing.njk`](/Users/yakhyo/Workspace/python-lessons/_includes/landing.njk) and wrapped by [`_layouts/base.njk`](/Users/yakhyo/Workspace/python-lessons/_layouts/base.njk). If the intended target is a different page, this PRD should be adapted before implementation. + +## Summary + +The current homepage is functional but structurally thin. It exposes tutorial lists and DSA links, yet it does not explain the product clearly in the first viewport, does not guide different learner types effectively, and leaves important content, trust, and SEO value in metadata rather than on-page content. + +The goal is to redesign the homepage so new visitors can immediately understand: + +1. What the site offers. +2. Who it is for. +3. Where to start. +4. What to do next. + +The implementation must be non-breaking: no URL changes, no collection changes, no disruption to localization, and no regressions to existing tutorial/tool access. + +## Background + +The site is an Eleventy + Tailwind content platform for Python learning in English and Uzbek. The homepage is the primary entry point for discovery but currently behaves more like a content index than a guided landing experience. + +Current homepage characteristics: + +- Navigation includes Tutorial, DSA, Online IDE, language switch, and GitHub CTA. +- Main body begins with a tutorial content list, level tabs, and long lesson lists. +- DSA content appears as a second section with two dense cards. +- Footer is comprehensive and functional. +- Root `/` redirects to `/en/`. + +## Current-State Findings + +### Content + +- There is no visible homepage `h1`; the page starts at `h2`, which weakens accessibility and SEO. +- The strongest value proposition exists in metadata, not in the page body. +- The page does not explain learner outcomes, estimated effort, or recommended starting points. +- Level descriptions are generic and do not help users self-select quickly. +- Tutorial titles are useful as an index, but the page relies too heavily on raw lists without summary, context, or prioritization. +- The site claims credibility in meta description ("Trusted by 100+ campuses & companies since 2015") but that proof is not visible on-page. + +### UI/UX + +- The first viewport lacks a clear hero, primary CTA, or structured path selection. +- The initial experience is list-heavy and scanning-intensive. +- DSA content competes with the main Python tutorial path instead of being clearly secondary or optional. +- There is no "start here" path for beginners, returning users, or DSA-focused learners. +- The page offers little progressive disclosure; users see long lists before they understand the structure. + +### Accessibility + +- No visible `h1` on the homepage. +- The level switcher is implemented as buttons with JavaScript display toggling, but it does not appear to use full tab semantics (`tablist`, `tab`, `tabpanel`, `aria-selected`, keyboard behavior). +- Important content depends on client-side enhancement to filter correctly. +- Dense list presentation increases cognitive load, especially on mobile. + +### SEO / Information Architecture + +- Title tag is serviceable but repetitive: "Python Programming Tutorial | Python Tutorial". +- On-page content does not fully support the keywords and intent implied in metadata. +- Homepage schema is present, but the on-page structure does not strongly reinforce course discovery or entry-point selection. + +### Data / Quality + +- English and Uzbek tutorial inventories are symmetrical: 21 basic, 11 intermediate, 12 advanced. +- Data structures list shows an ordering inconsistency in the rendered output (`06` appears twice), which reduces perceived polish. + +## Problem Statement + +New users land on the homepage and see a content index before they see a clear promise, path, or recommended next action. This slows comprehension, weakens conversion into learning starts, and makes the product feel less deliberate than the underlying content quality warrants. + +## Goals + +1. Clarify the homepage value proposition within the first viewport. +2. Help first-time users choose a learning path in under 5 seconds. +3. Increase clicks into the correct first lesson, DSA roadmap, or Online IDE. +4. Improve homepage accessibility, SEO, and perceived quality. +5. Preserve all existing routes, content collections, and localization behavior. + +## Non-Goals + +1. Rewriting all tutorial content in this phase. +2. Redesigning tutorial detail pages, tool pages, or DSA detail pages. +3. Changing existing URLs, slugs, or collection shapes. +4. Adding accounts, progress tracking, or a forum backend. +5. Replatforming away from Eleventy or Tailwind. + +## Primary Users + +### Beginner Learner + +Needs a clear starting point, reassurance that the content is free and structured, and low-friction entry into lesson 1. + +### Returning Learner + +Needs quick access to the next level, topic, or tool without re-reading the entire page. + +### DSA-Oriented Learner + +Needs a clear path into data structures and algorithms without confusing that track with the core beginner Python track. + +## User Stories + +1. As a beginner, I want to know where to start immediately so I can begin learning without comparing dozens of links. +2. As an intermediate learner, I want to jump to the right level quickly so I do not have to scan beginner content. +3. As a DSA learner, I want to understand whether DSA is part of the Python curriculum or a separate track. +4. As a bilingual user, I want the English and Uzbek homepages to feel equivalent in structure and intent. +5. As a mobile user, I want the homepage to feel manageable and readable without long scrolling through dense lists. + +## Success Metrics + +Primary metrics: + +1. Increase homepage click-through rate to the first tutorial lesson. +2. Increase click-through rate to level-specific entry points. +3. Increase click-through rate to DSA roadmap and Online IDE from relevant sections. + +Secondary metrics: + +1. Reduce bounce rate from homepage sessions. +2. Improve average time to first outbound educational click. +3. Improve accessibility audit scores for heading structure, focus, and semantic controls. + +## Experience Principles + +1. Explain before listing. +2. Guide before overwhelming. +3. Keep the primary path obvious. +4. Make every section answer "why should I click this?" +5. Default to server-rendered clarity; use JavaScript only for enhancement. +6. Preserve familiarity while increasing polish. + +## Proposed Information Architecture + +### 1. Hero Section + +Purpose: Establish the value proposition and primary next actions. + +Requirements: + +- Add a visible homepage `h1`. +- Add a concise subheading describing free, structured Python learning in English and Uzbek. +- Add a primary CTA: start the core tutorial path. +- Add a secondary CTA: explore DSA or open the Online IDE. +- Add lightweight trust or scale indicators only if they can be substantiated. + +Recommended content: + +- H1: outcome-focused and beginner-friendly. +- Supporting copy: structured curriculum, free access, bilingual support, practical progression. +- Utility points: number of lessons, levels, DSA track, browser IDE. + +### 2. Path Selection Section + +Purpose: Help users self-select instead of reading a long list first. + +Requirements: + +- Replace or reframe the current tab-first experience with level cards or a semantically correct tab/accordion pattern. +- Each path must include: + - audience + - prerequisites + - lesson count + - sample topics + - direct CTA +- Keep "Basic", "Intermediate", and "Advanced" or rename consistently across both languages, but do not mix naming patterns. + +Preferred approach: + +- Three path cards above the full lesson list. +- Each card expands into a curated preview. +- Full list remains available below for index-style browsing. + +### 3. Start-Here / Recommended Next Step Block + +Purpose: reduce decision friction. + +Requirements: + +- Add a compact guided starter flow for first-time users. +- Include a "Start here" recommendation for beginners. +- Include a "Skip to intermediate" route for users with prior experience. +- Include a "Practice in browser" route to the IDE. + +### 4. Curriculum Overview Section + +Purpose: keep the strong content inventory, but make it easier to scan. + +Requirements: + +- Preserve current lesson links and ordering. +- Introduce clearer grouping and spacing. +- Show counts per level. +- Consider previewing the first 5-7 lessons with a "View all lessons in this level" expansion pattern if needed. +- Standardize visible labels for readability and consistency. + +### 5. DSA Section + +Purpose: preserve discoverability without competing with the main tutorial funnel. + +Requirements: + +- Position DSA as a parallel or next-step learning track, not the default starting point for beginners. +- Add a short explanation of who the DSA content is for. +- Keep roadmap links intact. +- Fix ordering and display inconsistencies so the track feels curated. + +### 6. Bilingual Support Section or Inline Proof + +Purpose: make the bilingual value explicit. + +Requirements: + +- Surface language support in visible content, not only in the navbar switcher. +- Clarify that the site supports English and Uzbek learning paths. + +### 7. Footer / Contribution Framing + +Purpose: retain community and open-source value without distracting from the learning journey. + +Requirements: + +- Keep GitHub and contribution links. +- Reduce the likelihood that GitHub is interpreted as the primary CTA for learners. + +## Content Requirements + +### Messaging + +- Replace generic instructional copy with outcome-based copy. +- Avoid vague adjectives like "complete" unless backed by specifics. +- Explain what users will be able to do after each path. +- Keep copy concise, plain-language, and action-oriented. + +### Trust + +- Any claim such as "Trusted by 100+ campuses & companies since 2015" must either: + - be supported with evidence and shown on-page appropriately, or + - be removed/reworded until it can be validated. + +### Localization + +- English and Uzbek versions must remain structurally parallel. +- Copy should be localized intentionally, not mechanically translated. +- Shared content structures should come from data-driven templates where possible to avoid divergence. + +## UX Requirements + +1. The first screen must answer: + - what this site is + - who it is for + - where to start +2. The default next step for beginners must be obvious. +3. The page must remain easy to scan on mobile. +4. Dense link lists must be visually grouped and chunked. +5. Major actions must appear above the fold on common laptop and mobile widths. +6. Visual hierarchy must separate primary learning actions from secondary project/community actions. + +## Accessibility Requirements + +1. Add exactly one visible `h1` per homepage. +2. Use semantic section headings in descending order. +3. If tabs are used, implement full accessible tab semantics and keyboard behavior. +4. If tabs are not necessary, prefer simpler progressive disclosure such as cards plus anchored sections. +5. Ensure visible focus states for all links and buttons. +6. Meet WCAG AA contrast for text and interactive elements. +7. Ensure touch targets are at least 44x44 CSS pixels. +8. Avoid conveying state by color alone. +9. Preserve usability with JavaScript disabled where feasible; core links must remain visible and functional. + +## SEO Requirements + +1. Improve homepage title tag to remove redundancy and better match user intent. +2. Ensure on-page copy supports major search intents: + - learn python + - python tutorial + - python for beginners + - python lessons +3. Add a visible introductory paragraph aligned with metadata. +4. Preserve canonical URLs and current locale behavior. +5. Keep structured data valid; expand only if it maps to real on-page content. + +## Technical Requirements + +1. Do not change: + - tutorial permalinks + - DSA permalinks + - root redirect behavior + - English/Uzbek path conventions +2. Keep the homepage implementation compatible with existing Eleventy collection logic. +3. Prefer additive refactoring: + - extract reusable content config + - keep markup modular + - avoid mixing large inline strings with complex interactive logic +4. JavaScript should enhance, not gate, the core experience. +5. New UI should not require heavyweight client frameworks. +6. Preserve current navbar, footer, and tool links unless a specific improvement is justified in implementation review. + +## Recommended Implementation Approach + +### Phase 1: Content and Structure + +- Add hero section with `h1`, subhead, and primary CTAs. +- Add path-selection cards or improved level navigation. +- Reframe DSA as a secondary track. +- Add visible bilingual and trust messaging where valid. + +### Phase 2: Semantics and Interaction + +- Replace ad hoc tab behavior with accessible interaction patterns. +- Improve mobile spacing, chunking, and focus treatment. +- Keep content usable without JavaScript. + +### Phase 3: Polish and Measurement + +- Refine copy, spacing, iconography, and micro-interactions. +- Add analytics events if GA is enabled. +- Validate SEO, accessibility, and content parity. + +## Acceptance Criteria + +1. Homepage has a visible `h1` in both English and Uzbek. +2. First viewport contains a clear value proposition and at least one primary learning CTA. +3. Users can identify beginner, intermediate, advanced, and DSA paths without reading long lists first. +4. All current tutorial and roadmap links continue to work unchanged. +5. Homepage remains functional and understandable even if homepage enhancement JavaScript fails. +6. Keyboard navigation is complete and predictable. +7. Lighthouse/accessibility checks show no regressions on homepage-critical semantics. +8. Content claims visible on-page are accurate and reviewable. +9. English and Uzbek homepages ship with matching structure and equivalent intent. + +## Validation Checklist + +- Run `npm run build` +- Run `npm run check` +- Manually verify: + - `/` + - `/en/` + - `/uz/` + - mobile navbar behavior + - language switch behavior + - CTA links + - DSA links + - no-JS fallback behavior for homepage navigation + +## Risks + +1. Over-designing the homepage could hide the site's strongest asset: direct access to lessons. +2. Copy changes may create English/Uzbek parity drift if not centralized. +3. Adding interaction without semantic rigor could worsen accessibility. +4. Unverified trust claims could create credibility risk if made more prominent. +5. Excessive visual complexity could conflict with the site's current lightweight performance profile. + +## Open Questions + +1. Should the primary CTA go to lesson 1, the tutorial index, or a new curated "start here" anchor on the homepage? +2. Is the trust claim about campuses/companies validated and ready for on-page use? +3. Should DSA remain on the homepage, or should it be visually reduced and positioned as a next-step track? +4. Does the team want a simple homepage search/jump feature, or is guided navigation enough for this phase? +5. Should visible lesson labels be normalized for readability on the homepage even if source page titles stay unchanged? + +## Suggested Deliverables + +1. Updated homepage wireframe for desktop and mobile. +2. Finalized bilingual homepage copy. +3. Implementation ticket set: + - content strategy + - homepage template refactor + - accessibility pass + - analytics hooks + - QA and regression verification diff --git a/tools/online-python-ide.njk b/tools/online-python-ide.njk index afcd01e..463f510 100644 --- a/tools/online-python-ide.njk +++ b/tools/online-python-ide.njk @@ -1,7 +1,7 @@ --- layout: tool-fullscreen.njk -title: Online Python IDE - Web Integrated Development Environment -description: Full-featured web-based Python IDE with syntax highlighting, separate output panel, interactive terminal, and keyboard shortcuts. Run Python code instantly in your browser. +title: Python Lab - Online Python IDE and Browser REPL +description: Practice Python in Python Lab, a browser-based IDE with syntax highlighting, a separate output panel, an interactive terminal, and keyboard shortcuts. permalink: /online-python-ide/ ---
@@ -44,7 +44,7 @@ permalink: /online-python-ide/
-
# Python Online IDE
+            
# Python Lab
 # Press Ctrl+Enter (Cmd+Enter on Mac) to run
 
 class User: